Merge "Using material color in SettingsLib" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 85323c3..24cd610 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -171,6 +171,7 @@
// DeviceStateManager
aconfig_declarations {
name: "android.hardware.devicestate.feature.flags-aconfig",
+ exportable: true,
package: "android.hardware.devicestate.feature.flags",
srcs: ["core/java/android/hardware/devicestate/feature/*.aconfig"],
}
@@ -184,6 +185,7 @@
// Input
aconfig_declarations {
name: "com.android.hardware.input.input-aconfig",
+ exportable: true,
package: "com.android.hardware.input",
srcs: ["core/java/android/hardware/input/*.aconfig"],
}
@@ -456,6 +458,7 @@
// Hardware
aconfig_declarations {
name: "android.hardware.flags-aconfig",
+ exportable: true,
package: "android.hardware.flags",
srcs: ["core/java/android/hardware/flags/*.aconfig"],
}
@@ -548,6 +551,7 @@
// Media Editing
aconfig_declarations {
name: "com.android.media.flags.editing-aconfig",
+ exportable: true,
package: "com.android.media.editing.flags",
srcs: [
"media/java/android/media/flags/editing.aconfig",
@@ -593,6 +597,7 @@
// Media TV
aconfig_declarations {
name: "android.media.tv.flags-aconfig",
+ exportable: true,
package: "android.media.tv.flags",
srcs: ["media/java/android/media/tv/flags/media_tv.aconfig"],
}
@@ -606,6 +611,7 @@
// OnDeviceIntelligence
aconfig_declarations {
name: "android.app.ondeviceintelligence-aconfig",
+ exportable: true,
package: "android.app.ondeviceintelligence.flags",
srcs: ["core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig"],
}
@@ -635,6 +641,19 @@
],
}
+java_aconfig_library {
+ name: "android.permission.flags-aconfig-java-host",
+ aconfig_declarations: "android.permission.flags-aconfig",
+ host_supported: true,
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ min_sdk_version: "30",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ "com.android.nfcservices",
+ ],
+}
+
// SQLite
aconfig_declarations {
name: "android.database.sqlite-aconfig",
@@ -657,6 +676,7 @@
// Biometrics
aconfig_declarations {
name: "android.hardware.biometrics.flags-aconfig",
+ exportable: true,
package: "android.hardware.biometrics",
srcs: ["core/java/android/hardware/biometrics/flags.aconfig"],
}
@@ -734,6 +754,7 @@
// Broadcast Radio
aconfig_declarations {
name: "android.hardware.radio.flags-aconfig",
+ exportable: true,
package: "android.hardware.radio",
srcs: ["core/java/android/hardware/radio/*.aconfig"],
}
@@ -768,6 +789,7 @@
// Content Protection
aconfig_declarations {
name: "android.view.contentprotection.flags-aconfig",
+ exportable: true,
package: "android.view.contentprotection.flags",
srcs: ["core/java/android/view/contentprotection/flags/*.aconfig"],
}
@@ -794,6 +816,7 @@
// App prediction
aconfig_declarations {
name: "android.service.appprediction.flags-aconfig",
+ exportable: true,
package: "android.service.appprediction.flags",
srcs: ["core/java/android/service/appprediction/flags/*.aconfig"],
}
@@ -807,6 +830,7 @@
// Controls
aconfig_declarations {
name: "android.service.controls.flags-aconfig",
+ exportable: true,
package: "android.service.controls.flags",
srcs: ["core/java/android/service/controls/flags/*.aconfig"],
}
@@ -820,6 +844,7 @@
// Voice
aconfig_declarations {
name: "android.service.voice.flags-aconfig",
+ exportable: true,
package: "android.service.voice.flags",
srcs: ["core/java/android/service/voice/flags/*.aconfig"],
}
@@ -849,6 +874,7 @@
// Companion
aconfig_declarations {
name: "android.companion.flags-aconfig",
+ exportable: true,
package: "android.companion",
srcs: ["core/java/android/companion/*.aconfig"],
}
@@ -862,6 +888,7 @@
// Networking
aconfig_declarations {
name: "android.net.platform.flags-aconfig",
+ exportable: true,
package: "android.net.platform.flags",
srcs: ["core/java/android/net/flags.aconfig"],
visibility: [":__subpackages__"],
@@ -870,6 +897,7 @@
// Thread network
aconfig_declarations {
name: "com.android.net.thread.platform.flags-aconfig",
+ exportable: true,
package: "com.android.net.thread.platform.flags",
srcs: ["core/java/android/net/thread/flags.aconfig"],
}
@@ -967,6 +995,7 @@
aconfig_declarations {
name: "framework-jobscheduler-job.flags-aconfig",
package: "android.app.job",
+ exportable: true,
srcs: ["apex/jobscheduler/framework/aconfig/job.aconfig"],
}
@@ -1032,6 +1061,7 @@
// Smartspace
aconfig_declarations {
name: "android.app.smartspace.flags-aconfig",
+ exportable: true,
package: "android.app.smartspace.flags",
srcs: ["core/java/android/app/smartspace/flags.aconfig"],
}
@@ -1065,6 +1095,7 @@
// USB
aconfig_declarations {
name: "android.hardware.usb.flags-aconfig",
+ exportable: true,
package: "android.hardware.usb.flags",
srcs: ["core/java/android/hardware/usb/flags/*.aconfig"],
}
@@ -1145,6 +1176,7 @@
// Provider
aconfig_declarations {
name: "android.provider.flags-aconfig",
+ exportable: true,
package: "android.provider",
srcs: ["core/java/android/provider/*.aconfig"],
}
@@ -1165,6 +1197,7 @@
// Speech
aconfig_declarations {
name: "android.speech.flags-aconfig",
+ exportable: true,
package: "android.speech.flags",
srcs: ["core/java/android/speech/flags/*.aconfig"],
}
@@ -1185,6 +1218,7 @@
// Content
aconfig_declarations {
name: "android.content.flags-aconfig",
+ exportable: true,
package: "android.content.flags",
srcs: ["core/java/android/content/flags/flags.aconfig"],
}
@@ -1211,6 +1245,7 @@
// CrashRecovery Module
aconfig_declarations {
name: "android.crashrecovery.flags-aconfig",
+ exportable: true,
package: "android.crashrecovery.flags",
srcs: ["packages/CrashRecovery/aconfig/flags.aconfig"],
}
@@ -1221,6 +1256,13 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.crashrecovery.flags-aconfig-java-host",
+ aconfig_declarations: "android.crashrecovery.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ host_supported: true,
+}
+
// Backup
java_aconfig_library {
name: "backup_flags_lib",
@@ -1249,6 +1291,7 @@
// Wearable Sensing
aconfig_declarations {
name: "android.app.wearable.flags-aconfig",
+ exportable: true,
package: "android.app.wearable",
srcs: ["core/java/android/app/wearable/*.aconfig"],
}
diff --git a/apct-tests/perftests/inputmethod/AndroidManifest.xml b/apct-tests/perftests/inputmethod/AndroidManifest.xml
index 3eea418..34f9692 100644
--- a/apct-tests/perftests/inputmethod/AndroidManifest.xml
+++ b/apct-tests/perftests/inputmethod/AndroidManifest.xml
@@ -22,7 +22,8 @@
<application>
<uses-library android:name="android.test.runner" />
<activity android:name="android.perftests.utils.PerfTestActivity"
- android:exported="true">
+ android:theme="@android:style/Theme.DeviceDefault.Light.NoActionBar"
+ android:exported="true">
<intent-filter>
<action android:name="com.android.perftests.core.PERFTEST" />
</intent-filter>
diff --git a/apct-tests/perftests/packagemanager/AndroidTest.xml b/apct-tests/perftests/packagemanager/AndroidTest.xml
index c9d45a6..db938e4 100644
--- a/apct-tests/perftests/packagemanager/AndroidTest.xml
+++ b/apct-tests/perftests/packagemanager/AndroidTest.xml
@@ -76,11 +76,6 @@
<option name="test-file-name" value="QueriesAll49.apk"/>
</target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="com.android.perftests.packagemanager"/>
- <option name="hidden-api-checks" value="false"/>
- </test>
-
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="directory-keys" value="/data/local/PackageManagerPerfTests"/>
<option name="collect-on-run-ended-only" value="true"/>
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java b/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java
index f3bea17..0c2ee8c 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java
@@ -19,7 +19,9 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Insets;
import android.os.Bundle;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.LinearLayout;
@@ -42,6 +44,11 @@
if (getIntent().getBooleanExtra(INTENT_EXTRA_ADD_EDIT_TEXT, false)) {
final LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
+ layout.setOnApplyWindowInsetsListener((v, w) -> {
+ final Insets insets = w.getSystemWindowInsets();
+ v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ return WindowInsets.CONSUMED;
+ });
final EditText editText = new EditText(this);
editText.setId(ID_EDITOR);
diff --git a/apex/jobscheduler/service/aconfig/device_idle.aconfig b/apex/jobscheduler/service/aconfig/device_idle.aconfig
index e8c99b1..c4d0d18 100644
--- a/apex/jobscheduler/service/aconfig/device_idle.aconfig
+++ b/apex/jobscheduler/service/aconfig/device_idle.aconfig
@@ -2,6 +2,16 @@
container: "system"
flag {
+ name: "remove_idle_location"
+ namespace: "location"
+ description: "Remove DeviceIdleController usage of location"
+ bug: "332770178"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "disable_wakelocks_in_light_idle"
namespace: "backstage_power"
description: "Disable wakelocks for background apps while Light Device Idle is active"
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 4832ea6..11fa7b75 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -109,6 +109,7 @@
import com.android.server.am.BatteryStatsService;
import com.android.server.deviceidle.ConstraintController;
import com.android.server.deviceidle.DeviceIdleConstraintTracker;
+import com.android.server.deviceidle.Flags;
import com.android.server.deviceidle.IDeviceIdleConstraint;
import com.android.server.deviceidle.TvConstraintController;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -2558,7 +2559,7 @@
}
boolean isLocationPrefetchEnabled() {
- return mContext.getResources().getBoolean(
+ return !Flags.removeIdleLocation() && mContext.getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePrefetchLocation);
}
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 096238a..012ede2 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -1737,17 +1737,6 @@
continue;
}
- if (!nextPending.isReady()) {
- // This could happen when the job count reached its quota, the constrains
- // for the job has been updated but hasn't been removed from the pending
- // queue yet.
- if (DEBUG) {
- Slog.w(TAG, "Pending+not ready job: " + nextPending);
- }
- pendingJobQueue.remove(nextPending);
- continue;
- }
-
if (DEBUG && isSimilarJobRunningLocked(nextPending)) {
Slog.w(TAG, "Already running similar job to: " + nextPending);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index cfbfa5d..3c9648b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -512,7 +512,7 @@
/** An app has reached its quota. The message should contain a {@link UserPackage} object. */
@VisibleForTesting
- static final int MSG_REACHED_TIME_QUOTA = 0;
+ static final int MSG_REACHED_QUOTA = 0;
/** Drop any old timing sessions. */
private static final int MSG_CLEAN_UP_SESSIONS = 1;
/** Check if a package is now within its quota. */
@@ -524,7 +524,7 @@
* object.
*/
@VisibleForTesting
- static final int MSG_REACHED_EJ_TIME_QUOTA = 4;
+ static final int MSG_REACHED_EJ_QUOTA = 4;
/**
* Process a new {@link UsageEvents.Event}. The event will be the message's object and the
* userId will the first arg.
@@ -533,11 +533,6 @@
/** A UID's free quota grace period has ended. */
@VisibleForTesting
static final int MSG_END_GRACE_PERIOD = 6;
- /**
- * An app has reached its job count quota. The message should contain a {@link UserPackage}
- * object.
- */
- static final int MSG_REACHED_COUNT_QUOTA = 7;
public QuotaController(@NonNull JobSchedulerService service,
@NonNull BackgroundJobsController backgroundJobsController,
@@ -879,37 +874,17 @@
}
@VisibleForTesting
- @GuardedBy("mLock")
boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) {
final int standbyBucket = jobStatus.getEffectiveStandbyBucket();
// A job is within quota if one of the following is true:
// 1. it was started while the app was in the TOP state
// 2. the app is currently in the foreground
// 3. the app overall is within its quota
- if (jobStatus.shouldTreatAsUserInitiatedJob()
+ return jobStatus.shouldTreatAsUserInitiatedJob()
|| isTopStartedJobLocked(jobStatus)
- || isUidInForeground(jobStatus.getSourceUid())) {
- return true;
- }
-
- if (standbyBucket == NEVER_INDEX) return false;
-
- if (isQuotaFreeLocked(standbyBucket)) return true;
-
- final ExecutionStats stats = getExecutionStatsLocked(jobStatus.getSourceUserId(),
- jobStatus.getSourcePackageName(), standbyBucket);
- if (!(getRemainingExecutionTimeLocked(stats) > 0)) {
- // Out of execution time quota.
- return false;
- }
-
- if (mService.isCurrentlyRunningLocked(jobStatus)) {
- // if job is running, considered as in quota so it can keep running.
- return true;
- }
-
- // Check if the app is within job count quota.
- return isUnderJobCountQuotaLocked(stats) && isUnderSessionCountQuotaLocked(stats);
+ || isUidInForeground(jobStatus.getSourceUid())
+ || isWithinQuotaLocked(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
}
@GuardedBy("mLock")
@@ -934,11 +909,12 @@
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
// TODO: use a higher minimum remaining time for jobs with MINIMUM priority
return getRemainingExecutionTimeLocked(stats) > 0
- && isUnderJobCountQuotaLocked(stats)
- && isUnderSessionCountQuotaLocked(stats);
+ && isUnderJobCountQuotaLocked(stats, standbyBucket)
+ && isUnderSessionCountQuotaLocked(stats, standbyBucket);
}
- private boolean isUnderJobCountQuotaLocked(@NonNull ExecutionStats stats) {
+ private boolean isUnderJobCountQuotaLocked(@NonNull ExecutionStats stats,
+ final int standbyBucket) {
final long now = sElapsedRealtimeClock.millis();
final boolean isUnderAllowedTimeQuota =
(stats.jobRateLimitExpirationTimeElapsed <= now
@@ -947,7 +923,8 @@
&& stats.bgJobCountInWindow < stats.jobCountLimit;
}
- private boolean isUnderSessionCountQuotaLocked(@NonNull ExecutionStats stats) {
+ private boolean isUnderSessionCountQuotaLocked(@NonNull ExecutionStats stats,
+ final int standbyBucket) {
final long now = sElapsedRealtimeClock.millis();
final boolean isUnderAllowedTimeQuota = (stats.sessionRateLimitExpirationTimeElapsed <= now
|| stats.sessionCountInRateLimitingWindow < mMaxSessionCountPerRateLimitingWindow);
@@ -1472,7 +1449,6 @@
stats.jobCountInRateLimitingWindow = 0;
}
stats.jobCountInRateLimitingWindow += count;
- stats.bgJobCountInWindow += count;
}
}
@@ -1707,11 +1683,10 @@
changedJobs.add(js);
}
} else if (realStandbyBucket != EXEMPTED_INDEX && realStandbyBucket != ACTIVE_INDEX
- && realStandbyBucket == js.getEffectiveStandbyBucket()
- && !mService.isCurrentlyRunningLocked(js)) {
+ && realStandbyBucket == js.getEffectiveStandbyBucket()) {
// An app in the ACTIVE bucket may be out of quota while the job could be in quota
// for some reason. Therefore, avoid setting the real value here and check each job
- // individually. Running job need to determine its own quota status as well.
+ // individually.
if (setConstraintSatisfied(js, nowElapsed, realInQuota, isWithinEJQuota)) {
changedJobs.add(js);
}
@@ -1830,8 +1805,9 @@
}
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
- final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats);
- final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats);
+ final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats, standbyBucket);
+ final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats,
+ standbyBucket);
final long remainingEJQuota = getRemainingEJExecutionTimeLocked(userId, packageName);
final boolean inRegularQuota =
@@ -2150,11 +2126,6 @@
mBgJobCount++;
if (mRegularJobTimer) {
incrementJobCountLocked(mPkg.userId, mPkg.packageName, 1);
- final ExecutionStats stats = getExecutionStatsLocked(mPkg.userId,
- mPkg.packageName, jobStatus.getEffectiveStandbyBucket(), false);
- if (stats.bgJobCountInWindow >= stats.jobCountLimit) {
- mHandler.obtainMessage(MSG_REACHED_COUNT_QUOTA, mPkg).sendToTarget();
- }
}
if (mRunningBgJobs.size() == 1) {
// Started tracking the first job.
@@ -2286,6 +2257,7 @@
// repeatedly plugged in and unplugged, or an app changes foreground state
// very frequently, the job count for a package may be artificially high.
mBgJobCount = mRunningBgJobs.size();
+
if (mRegularJobTimer) {
incrementJobCountLocked(mPkg.userId, mPkg.packageName, mBgJobCount);
// Starting the timer means that all cached execution stats are now
@@ -2312,8 +2284,7 @@
return;
}
Message msg = mHandler.obtainMessage(
- mRegularJobTimer ? MSG_REACHED_TIME_QUOTA : MSG_REACHED_EJ_TIME_QUOTA,
- mPkg);
+ mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_EJ_QUOTA, mPkg);
final long timeRemainingMs = mRegularJobTimer
? getTimeUntilQuotaConsumedLocked(mPkg.userId, mPkg.packageName)
: getTimeUntilEJQuotaConsumedLocked(mPkg.userId, mPkg.packageName);
@@ -2330,7 +2301,7 @@
private void cancelCutoff() {
mHandler.removeMessages(
- mRegularJobTimer ? MSG_REACHED_TIME_QUOTA : MSG_REACHED_EJ_TIME_QUOTA, mPkg);
+ mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_EJ_QUOTA, mPkg);
}
public void dump(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
@@ -2586,7 +2557,7 @@
break;
default:
if (DEBUG) {
- Slog.d(TAG, "Dropping usage event " + event.getEventType());
+ Slog.d(TAG, "Dropping event " + event.getEventType());
}
break;
}
@@ -2695,7 +2666,7 @@
public void handleMessage(Message msg) {
synchronized (mLock) {
switch (msg.what) {
- case MSG_REACHED_TIME_QUOTA: {
+ case MSG_REACHED_QUOTA: {
UserPackage pkg = (UserPackage) msg.obj;
if (DEBUG) {
Slog.d(TAG, "Checking if " + pkg + " has reached its quota.");
@@ -2714,7 +2685,7 @@
// This could potentially happen if an old session phases out while a
// job is currently running.
// Reschedule message
- Message rescheduleMsg = obtainMessage(MSG_REACHED_TIME_QUOTA, pkg);
+ Message rescheduleMsg = obtainMessage(MSG_REACHED_QUOTA, pkg);
timeRemainingMs = getTimeUntilQuotaConsumedLocked(pkg.userId,
pkg.packageName);
if (DEBUG) {
@@ -2724,7 +2695,7 @@
}
break;
}
- case MSG_REACHED_EJ_TIME_QUOTA: {
+ case MSG_REACHED_EJ_QUOTA: {
UserPackage pkg = (UserPackage) msg.obj;
if (DEBUG) {
Slog.d(TAG, "Checking if " + pkg + " has reached its EJ quota.");
@@ -2742,7 +2713,7 @@
// This could potentially happen if an old session phases out while a
// job is currently running.
// Reschedule message
- Message rescheduleMsg = obtainMessage(MSG_REACHED_EJ_TIME_QUOTA, pkg);
+ Message rescheduleMsg = obtainMessage(MSG_REACHED_EJ_QUOTA, pkg);
timeRemainingMs = getTimeUntilEJQuotaConsumedLocked(
pkg.userId, pkg.packageName);
if (DEBUG) {
@@ -2752,18 +2723,6 @@
}
break;
}
- case MSG_REACHED_COUNT_QUOTA: {
- UserPackage pkg = (UserPackage) msg.obj;
- if (DEBUG) {
- Slog.d(TAG, pkg + " has reached its count quota.");
- }
-
- mStateChangedListener.onControllerStateChanged(
- maybeUpdateConstraintForPkgLocked(
- sElapsedRealtimeClock.millis(),
- pkg.userId, pkg.packageName));
- break;
- }
case MSG_CLEAN_UP_SESSIONS:
if (DEBUG) {
Slog.d(TAG, "Cleaning up timing sessions.");
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 613678b..410074e 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1989,6 +1989,9 @@
mAdminProtectedPackages.put(userId, packageNames);
}
}
+ if (android.app.admin.flags.Flags.disallowUserControlBgUsageFix()) {
+ postCheckIdleStates(userId);
+ }
}
@Override
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 1b1bc6b..f56a950 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -54,34 +54,34 @@
baseline_file: ":non-updatable-lint-baseline.txt",
},
},
- dists: [
- {
- targets: ["sdk"],
- dir: "apistubs/android/public/api",
- dest: "android-non-updatable.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/public/api",
- dest: "android-non-updatable-removed.txt",
- },
- ],
soong_config_variables: {
release_hidden_api_exportable_stubs: {
dists: [
{
+ targets: ["sdk"],
+ dir: "apistubs/android/public/api",
+ dest: "android-non-updatable.txt",
tag: ".exportable.api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/public/api",
+ dest: "android-non-updatable-removed.txt",
tag: ".exportable.removed-api.txt",
},
],
conditions_default: {
dists: [
{
+ targets: ["sdk"],
+ dir: "apistubs/android/public/api",
+ dest: "android-non-updatable.txt",
tag: ".api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/public/api",
+ dest: "android-non-updatable-removed.txt",
tag: ".removed-api.txt",
},
],
@@ -134,34 +134,34 @@
baseline_file: ":non-updatable-system-lint-baseline.txt",
},
},
- dists: [
- {
- targets: ["sdk"],
- dir: "apistubs/android/system/api",
- dest: "android-non-updatable.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/system/api",
- dest: "android-non-updatable-removed.txt",
- },
- ],
soong_config_variables: {
release_hidden_api_exportable_stubs: {
dists: [
{
+ targets: ["sdk"],
+ dir: "apistubs/android/system/api",
+ dest: "android-non-updatable.txt",
tag: ".exportable.api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/system/api",
+ dest: "android-non-updatable-removed.txt",
tag: ".exportable.removed-api.txt",
},
],
conditions_default: {
dists: [
{
+ targets: ["sdk"],
+ dir: "apistubs/android/system/api",
+ dest: "android-non-updatable.txt",
tag: ".api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/system/api",
+ dest: "android-non-updatable-removed.txt",
tag: ".removed-api.txt",
},
],
@@ -189,56 +189,58 @@
baseline_file: ":non-updatable-test-lint-baseline.txt",
},
},
- dists: [
- {
- targets: ["sdk"],
- dir: "apistubs/android/test/api",
- dest: "android.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/test/api",
- dest: "removed.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/test/api",
- dest: "android-non-updatable.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/test/api",
- dest: "android-non-updatable-removed.txt",
- },
- ],
soong_config_variables: {
release_hidden_api_exportable_stubs: {
dists: [
{
+ targets: ["sdk"],
+ dir: "apistubs/android/test/api",
+ dest: "android.txt",
tag: ".exportable.api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/test/api",
+ dest: "removed.txt",
tag: ".exportable.removed-api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/test/api",
+ dest: "android-non-updatable.txt",
tag: ".exportable.api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/test/api",
+ dest: "android-non-updatable-removed.txt",
tag: ".exportable.removed-api.txt",
},
],
conditions_default: {
dists: [
{
+ targets: ["sdk"],
+ dir: "apistubs/android/test/api",
+ dest: "android.txt",
tag: ".api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/test/api",
+ dest: "removed.txt",
tag: ".removed-api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/test/api",
+ dest: "android-non-updatable.txt",
tag: ".api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/test/api",
+ dest: "android-non-updatable-removed.txt",
tag: ".removed-api.txt",
},
],
@@ -271,34 +273,34 @@
baseline_file: ":non-updatable-module-lib-lint-baseline.txt",
},
},
- dists: [
- {
- targets: ["sdk"],
- dir: "apistubs/android/module-lib/api",
- dest: "android-non-updatable.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/module-lib/api",
- dest: "android-non-updatable-removed.txt",
- },
- ],
soong_config_variables: {
release_hidden_api_exportable_stubs: {
dists: [
{
+ targets: ["sdk"],
+ dir: "apistubs/android/module-lib/api",
+ dest: "android-non-updatable.txt",
tag: ".exportable.api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/module-lib/api",
+ dest: "android-non-updatable-removed.txt",
tag: ".exportable.removed-api.txt",
},
],
conditions_default: {
dists: [
{
+ targets: ["sdk"],
+ dir: "apistubs/android/module-lib/api",
+ dest: "android-non-updatable.txt",
tag: ".api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/module-lib/api",
+ dest: "android-non-updatable-removed.txt",
tag: ".removed-api.txt",
},
],
diff --git a/api/coverage/tools/Android.bp b/api/coverage/tools/Android.bp
index 3e16912..caaca99 100644
--- a/api/coverage/tools/Android.bp
+++ b/api/coverage/tools/Android.bp
@@ -30,3 +30,24 @@
type: "full",
},
}
+
+java_test_host {
+ name: "extract-flagged-apis-test",
+ srcs: ["ExtractFlaggedApisTest.kt"],
+ libs: [
+ "extract_flagged_apis_proto",
+ "junit",
+ "libprotobuf-java-full",
+ ],
+ static_libs: [
+ "truth",
+ "truth-liteproto-extension",
+ "truth-proto-extension",
+ ],
+ data: [
+ ":extract-flagged-apis",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+}
diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt
index d5adfd0..5efda98 100644
--- a/api/coverage/tools/ExtractFlaggedApis.kt
+++ b/api/coverage/tools/ExtractFlaggedApis.kt
@@ -17,6 +17,7 @@
package android.platform.coverage
import com.android.tools.metalava.model.ClassItem
+import com.android.tools.metalava.model.Item
import com.android.tools.metalava.model.MethodItem
import com.android.tools.metalava.model.text.ApiFile
import java.io.File
@@ -28,12 +29,10 @@
val builder = FlagApiMap.newBuilder()
for (pkg in cb.getPackages().packages) {
val packageName = pkg.qualifiedName()
- pkg.allClasses()
- .filter { it.methods().size > 0 }
- .forEach {
- extractFlaggedApisFromClass(it, it.methods(), packageName, builder)
- extractFlaggedApisFromClass(it, it.constructors(), packageName, builder)
- }
+ pkg.allClasses().forEach {
+ extractFlaggedApisFromClass(it, it.methods(), packageName, builder)
+ extractFlaggedApisFromClass(it, it.constructors(), packageName, builder)
+ }
}
val flagApiMap = builder.build()
FileWriter(args[1]).use { it.write(flagApiMap.toString()) }
@@ -45,20 +44,10 @@
packageName: String,
builder: FlagApiMap.Builder
) {
- val classFlag =
- classItem.modifiers
- .findAnnotation("android.annotation.FlaggedApi")
- ?.findAttribute("value")
- ?.value
- ?.value() as? String
+ if (methods.isEmpty()) return
+ val classFlag = getClassFlag(classItem)
for (method in methods) {
- val methodFlag =
- method.modifiers
- .findAnnotation("android.annotation.FlaggedApi")
- ?.findAttribute("value")
- ?.value
- ?.value() as? String
- ?: classFlag
+ val methodFlag = getFlagAnnotation(method) ?: classFlag
val api =
JavaMethod.newBuilder()
.setPackageName(packageName)
@@ -82,3 +71,23 @@
builder.putFlagToApi(flag, apis)
}
}
+
+fun getClassFlag(classItem: ClassItem): String? {
+ var classFlag = getFlagAnnotation(classItem)
+ var cur = classItem
+ // If a class is not an inner class, use its @FlaggedApi annotation value.
+ // Otherwise, use the flag value of the closest outer class that is annotated by @FlaggedApi.
+ while (cur.isInnerClass() && classFlag == null) {
+ cur = cur.parent() as ClassItem
+ classFlag = getFlagAnnotation(cur)
+ }
+ return classFlag
+}
+
+fun getFlagAnnotation(item: Item): String? {
+ return item.modifiers
+ .findAnnotation("android.annotation.FlaggedApi")
+ ?.findAttribute("value")
+ ?.value
+ ?.value() as? String
+}
diff --git a/api/coverage/tools/ExtractFlaggedApisTest.kt b/api/coverage/tools/ExtractFlaggedApisTest.kt
new file mode 100644
index 0000000..427be36
--- /dev/null
+++ b/api/coverage/tools/ExtractFlaggedApisTest.kt
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.platform.coverage
+
+import com.google.common.truth.extensions.proto.ProtoTruth.assertThat
+import com.google.protobuf.TextFormat
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.StandardOpenOption
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ExtractFlaggedApisTest {
+
+ companion object {
+ const val COMMAND = "java -jar extract-flagged-apis.jar %s %s"
+ }
+
+ private var apiTextFile: Path = Files.createTempFile("current", ".txt")
+ private var flagToApiMap: Path = Files.createTempFile("flag_api_map", ".textproto")
+
+ @Before
+ fun setup() {
+ apiTextFile = Files.createTempFile("current", ".txt")
+ flagToApiMap = Files.createTempFile("flag_api_map", ".textproto")
+ }
+
+ @After
+ fun cleanup() {
+ Files.deleteIfExists(apiTextFile)
+ Files.deleteIfExists(flagToApiMap)
+ }
+
+ @Test
+ fun extractFlaggedApis_onlyMethodFlag_useMethodFlag() {
+ val apiText =
+ """
+ // Signature format: 2.0
+ package android.net.ipsec.ike {
+ public final class IkeSession implements java.lang.AutoCloseable {
+ method @FlaggedApi("com.android.ipsec.flags.dumpsys_api") public void dump(@NonNull java.io.PrintWriter);
+ }
+ }
+ """
+ .trimIndent()
+ Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND)
+
+ val process = Runtime.getRuntime().exec(createCommand())
+ process.waitFor()
+
+ val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8)
+ val result = TextFormat.parse(content, FlagApiMap::class.java)
+
+ val expected = FlagApiMap.newBuilder()
+ val api =
+ JavaMethod.newBuilder()
+ .setPackageName("android.net.ipsec.ike")
+ .setClassName("IkeSession")
+ .setMethodName("dump")
+ api.addParameters("java.io.PrintWriter")
+ addFlaggedApi(expected, api, "com.android.ipsec.flags.dumpsys_api")
+ assertThat(result).isEqualTo(expected.build())
+ }
+
+ @Test
+ fun extractFlaggedApis_onlyClassFlag_useClassFlag() {
+ val apiText =
+ """
+ // Signature format: 2.0
+ package android.net.ipsec.ike {
+ @FlaggedApi("com.android.ipsec.flags.dumpsys_api") public final class IkeSession implements java.lang.AutoCloseable {
+ method public void dump(@NonNull java.io.PrintWriter);
+ }
+ }
+ """
+ .trimIndent()
+ Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND)
+
+ val process = Runtime.getRuntime().exec(createCommand())
+ process.waitFor()
+
+ val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8)
+ val result = TextFormat.parse(content, FlagApiMap::class.java)
+
+ val expected = FlagApiMap.newBuilder()
+ val api =
+ JavaMethod.newBuilder()
+ .setPackageName("android.net.ipsec.ike")
+ .setClassName("IkeSession")
+ .setMethodName("dump")
+ api.addParameters("java.io.PrintWriter")
+ addFlaggedApi(expected, api, "com.android.ipsec.flags.dumpsys_api")
+ assertThat(result).isEqualTo(expected.build())
+ }
+
+ @Test
+ fun extractFlaggedApis_flaggedConstructorsAreFlaggedApis() {
+ val apiText =
+ """
+ // Signature format: 2.0
+ package android.app.pinner {
+ @FlaggedApi("android.app.pinner_service_client_api") public class PinnerServiceClient {
+ ctor @FlaggedApi("android.app.pinner_service_client_api") public PinnerServiceClient();
+ }
+ }
+ """
+ .trimIndent()
+ Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND)
+
+ val process = Runtime.getRuntime().exec(createCommand())
+ process.waitFor()
+
+ val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8)
+ val result = TextFormat.parse(content, FlagApiMap::class.java)
+
+ val expected = FlagApiMap.newBuilder()
+ val api =
+ JavaMethod.newBuilder()
+ .setPackageName("android.app.pinner")
+ .setClassName("PinnerServiceClient")
+ .setMethodName("PinnerServiceClient")
+ addFlaggedApi(expected, api, "android.app.pinner_service_client_api")
+ assertThat(result).isEqualTo(expected.build())
+ }
+
+ @Test
+ fun extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag() {
+ val apiText =
+ """
+ // Signature format: 2.0
+ package android.location.provider {
+ @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public final class ForwardGeocodeRequest implements android.os.Parcelable {
+ method public int describeContents();
+ }
+ public static final class ForwardGeocodeRequest.Builder {
+ method @NonNull public android.location.provider.ForwardGeocodeRequest build();
+ }
+ }
+ """
+ .trimIndent()
+ Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND)
+
+ val process = Runtime.getRuntime().exec(createCommand())
+ process.waitFor()
+
+ val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8)
+ val result = TextFormat.parse(content, FlagApiMap::class.java)
+
+ val expected = FlagApiMap.newBuilder()
+ val api1 =
+ JavaMethod.newBuilder()
+ .setPackageName("android.location.provider")
+ .setClassName("ForwardGeocodeRequest")
+ .setMethodName("describeContents")
+ addFlaggedApi(expected, api1, "Flags.FLAG_NEW_GEOCODER")
+ val api2 =
+ JavaMethod.newBuilder()
+ .setPackageName("android.location.provider")
+ .setClassName("ForwardGeocodeRequest.Builder")
+ .setMethodName("build")
+ addFlaggedApi(expected, api2, "Flags.FLAG_NEW_GEOCODER")
+ assertThat(result).ignoringRepeatedFieldOrder().isEqualTo(expected.build())
+ }
+
+ @Test
+ fun extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag_deeplyNested() {
+ val apiText =
+ """
+ // Signature format: 2.0
+ package android.package.xyz {
+ @FlaggedApi(outer_class_flag) public final class OuterClass {
+ method public int apiInOuterClass();
+ }
+ public final class OuterClass.Deeply.NestedClass {
+ method public void apiInNestedClass();
+ }
+ }
+ """
+ .trimIndent()
+ Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND)
+
+ val process = Runtime.getRuntime().exec(createCommand())
+ process.waitFor()
+
+ val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8)
+ val result = TextFormat.parse(content, FlagApiMap::class.java)
+
+ val expected = FlagApiMap.newBuilder()
+ val api1 =
+ JavaMethod.newBuilder()
+ .setPackageName("android.package.xyz")
+ .setClassName("OuterClass")
+ .setMethodName("apiInOuterClass")
+ addFlaggedApi(expected, api1, "outer_class_flag")
+ val api2 =
+ JavaMethod.newBuilder()
+ .setPackageName("android.package.xyz")
+ .setClassName("OuterClass.Deeply.NestedClass")
+ .setMethodName("apiInNestedClass")
+ addFlaggedApi(expected, api2, "outer_class_flag")
+ assertThat(result).ignoringRepeatedFieldOrder().isEqualTo(expected.build())
+ }
+
+ private fun addFlaggedApi(builder: FlagApiMap.Builder, api: JavaMethod.Builder, flag: String) {
+ if (builder.containsFlagToApi(flag)) {
+ val updatedApis =
+ builder.getFlagToApiOrThrow(flag).toBuilder().addJavaMethods(api).build()
+ builder.putFlagToApi(flag, updatedApis)
+ } else {
+ val apis = FlaggedApis.newBuilder().addJavaMethods(api).build()
+ builder.putFlagToApi(flag, apis)
+ }
+ }
+
+ private fun createCommand(): Array<String> {
+ val command =
+ String.format(COMMAND, apiTextFile.toAbsolutePath(), flagToApiMap.toAbsolutePath())
+ return command.split(" ").toTypedArray()
+ }
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index 13d5e03..c86e4cd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -282,7 +282,7 @@
field public static final String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
field public static final String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES";
field public static final String REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE = "android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE";
- field @FlaggedApi("android.companion.flags.device_presence") public static final String REQUEST_OBSERVE_DEVICE_UUID_PRESENCE = "android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE";
+ field @FlaggedApi("android.companion.device_presence") public static final String REQUEST_OBSERVE_DEVICE_UUID_PRESENCE = "android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE";
field public static final String REQUEST_PASSWORD_COMPLEXITY = "android.permission.REQUEST_PASSWORD_COMPLEXITY";
field @Deprecated public static final String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
field public static final String RUN_USER_INITIATED_JOBS = "android.permission.RUN_USER_INITIATED_JOBS";
@@ -39388,10 +39388,8 @@
}
public final class FileIntegrityManager {
- method @FlaggedApi("android.security.fsverity_api") @Nullable public byte[] getFsVerityDigest(@NonNull java.io.File) throws java.io.IOException;
method public boolean isApkVeritySupported();
method @Deprecated @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("android.security.fsverity_api") public void setupFsVerity(@NonNull java.io.File) throws java.io.IOException;
}
public final class KeyChain {
@@ -54479,7 +54477,6 @@
field @FlaggedApi("com.android.window.flags.cover_display_opt_in") public static final int COMPAT_SMALL_COVER_SCREEN_OPT_IN = 1; // 0x1
field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
- field @FlaggedApi("com.android.window.flags.untrusted_embedding_state_sharing") public static final String PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING = "android.window.PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING";
field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION";
field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH";
field public static final String PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE = "android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b767c52..45bcd0d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -355,6 +355,7 @@
field public static final String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
field public static final String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
field public static final String SERIAL_PORT = "android.permission.SERIAL_PORT";
+ field @FlaggedApi("android.security.fsverity_api") public static final String SETUP_FSVERITY = "android.permission.SETUP_FSVERITY";
field public static final String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
field public static final String SET_CLIP_SOURCE = "android.permission.SET_CLIP_SOURCE";
field public static final String SET_DEFAULT_ACCOUNT_FOR_CONTACTS = "android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS";
@@ -12106,6 +12107,11 @@
package android.security {
+ public final class FileIntegrityManager {
+ method @FlaggedApi("android.security.fsverity_api") @Nullable public byte[] getFsVerityDigest(@NonNull java.io.File) throws java.io.IOException;
+ method @FlaggedApi("android.security.fsverity_api") public void setupFsVerity(@NonNull java.io.File) throws java.io.IOException;
+ }
+
public final class KeyChain {
method @Nullable @WorkerThread public static String getWifiKeyGrantAsUser(@NonNull android.content.Context, @NonNull android.os.UserHandle, @NonNull String);
method @WorkerThread public static boolean hasWifiKeyGrantAsUser(@NonNull android.content.Context, @NonNull android.os.UserHandle, @NonNull String);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 3b988e1..1383096 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -589,6 +589,7 @@
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
+ method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public void forceSetMaxPolicyStorageLimit(int);
method public void forceUpdateUserSetupComplete(int);
method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
method @Deprecated public int getDeviceOwnerType(@NonNull android.content.ComponentName);
@@ -599,6 +600,7 @@
method public long getLastSecurityLogRetrievalTime();
method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public java.util.Set<java.lang.String> getPolicyExemptApps();
+ method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public int getPolicySizeForAdmin(@NonNull android.app.admin.EnforcingAdmin);
method public boolean isCurrentInputMethodSetByOwner();
method public boolean isFactoryResetProtectionPolicySupported();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isNewUserDisclaimerAcknowledged();
@@ -667,6 +669,10 @@
field @NonNull public static final android.app.admin.DpcAuthority DPC_AUTHORITY;
}
+ public final class EnforcingAdmin implements android.os.Parcelable {
+ ctor @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") public EnforcingAdmin(@NonNull String, @NonNull android.app.admin.Authority, @NonNull android.os.UserHandle, @Nullable android.content.ComponentName);
+ }
+
public final class FlagUnion extends android.app.admin.ResolutionMechanism<java.lang.Integer> {
method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -1763,14 +1769,16 @@
}
public final class InputManager {
- method public void addUniqueIdAssociation(@NonNull String, @NonNull String);
+ method @FlaggedApi("com.android.input.flags.device_associations") @RequiresPermission("android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY") public void addUniqueIdAssociationByDescriptor(@NonNull String, @NonNull String);
+ method @FlaggedApi("com.android.input.flags.device_associations") @RequiresPermission("android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY") public void addUniqueIdAssociationByPort(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.REMAP_MODIFIER_KEYS) public void clearAllModifierKeyRemappings();
method @NonNull public java.util.List<java.lang.String> getKeyboardLayoutDescriptors();
method @NonNull public String getKeyboardLayoutTypeForLayoutDescriptor(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.REMAP_MODIFIER_KEYS) public java.util.Map<java.lang.Integer,java.lang.Integer> getModifierKeyRemapping();
method public int getMousePointerSpeed();
method @RequiresPermission(android.Manifest.permission.REMAP_MODIFIER_KEYS) public void remapModifierKey(int, int);
- method public void removeUniqueIdAssociation(@NonNull String);
+ method @FlaggedApi("com.android.input.flags.device_associations") @RequiresPermission("android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY") public void removeUniqueIdAssociationByDescriptor(@NonNull String);
+ method @FlaggedApi("com.android.input.flags.device_associations") @RequiresPermission("android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY") public void removeUniqueIdAssociationByPort(@NonNull String);
field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
}
@@ -4129,7 +4137,6 @@
method @NonNull public static String typeToString(int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.window.BackNavigationInfo> CREATOR;
- field public static final String KEY_TRIGGER_BACK = "TriggerBack";
field public static final int TYPE_CALLBACK = 4; // 0x4
field public static final int TYPE_CROSS_ACTIVITY = 2; // 0x2
field public static final int TYPE_CROSS_TASK = 3; // 0x3
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 685ea63..e6265ae 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -977,8 +977,16 @@
Method 'setHdmiCecVersion' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.hardware.input.InputManager#addUniqueIdAssociation(String, String):
Method 'addUniqueIdAssociation' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.input.InputManager#addUniqueIdAssociationByDescriptor(String, String):
+ Method 'addUniqueIdAssociationByDescriptor' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.input.InputManager#addUniqueIdAssociationByPort(String, String):
+ Method 'addUniqueIdAssociationByPort' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.hardware.input.InputManager#removeUniqueIdAssociation(String):
Method 'removeUniqueIdAssociation' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.input.InputManager#removeUniqueIdAssociationByDescriptor(String):
+ Method 'removeUniqueIdAssociationByDescriptor' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.input.InputManager#removeUniqueIdAssociationByPort(String):
+ Method 'removeUniqueIdAssociationByPort' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.hardware.location.GeofenceHardware#addGeofence(int, int, android.hardware.location.GeofenceHardwareRequest, android.hardware.location.GeofenceHardwareCallback):
Method 'addGeofence' documentation mentions permissions without declaring @RequiresPermission
RequiresPermission: android.hardware.location.GeofenceHardware#getMonitoringTypes():
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3575545..eaa23b9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2608,7 +2608,14 @@
break;
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
- mTransactionExecutor.execute(transaction);
+ final ClientTransactionListenerController controller =
+ ClientTransactionListenerController.getInstance();
+ controller.onClientTransactionStarted();
+ try {
+ mTransactionExecutor.execute(transaction);
+ } finally {
+ controller.onClientTransactionFinished();
+ }
if (isSystem()) {
// Client transactions inside system process are recycled on the client side
// instead of ClientLifecycleManager to avoid being cleared before this
@@ -3720,12 +3727,6 @@
return mActivities.get(token);
}
- @Nullable
- @Override
- public Context getWindowContext(@NonNull IBinder clientToken) {
- return WindowTokenClientController.getInstance().getWindowContext(clientToken);
- }
-
@VisibleForTesting(visibility = PACKAGE)
public Configuration getConfiguration() {
return mConfigurationController.getConfiguration();
@@ -6747,6 +6748,21 @@
void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r,
@NonNull Configuration overrideConfig, int displayId,
@NonNull ActivityWindowInfo activityWindowInfo, boolean alwaysReportChange) {
+ final ClientTransactionListenerController controller =
+ ClientTransactionListenerController.getInstance();
+ final Context contextToUpdate = r.activity;
+ controller.onContextConfigurationPreChanged(contextToUpdate);
+ try {
+ handleActivityConfigurationChangedInner(r, overrideConfig, displayId,
+ activityWindowInfo, alwaysReportChange);
+ } finally {
+ controller.onContextConfigurationPostChanged(contextToUpdate);
+ }
+ }
+
+ private void handleActivityConfigurationChangedInner(@NonNull ActivityClientRecord r,
+ @NonNull Configuration overrideConfig, int displayId,
+ @NonNull ActivityWindowInfo activityWindowInfo, boolean alwaysReportChange) {
synchronized (mPendingOverrideConfigs) {
final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(r.token);
if (overrideConfig.isOtherSeqNewer(pendingOverrideConfig)) {
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 308178c..76d6547 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -671,7 +671,7 @@
private String mName;
private ComponentName mOwner;
private Uri mConditionId;
- private int mInterruptionFilter;
+ private int mInterruptionFilter = NotificationManager.INTERRUPTION_FILTER_PRIORITY;
private boolean mEnabled = true;
private ComponentName mConfigurationActivity = null;
private ZenPolicy mPolicy = null;
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 01153c9..f0c3196 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -23,7 +23,6 @@
import android.app.servertransaction.DestroyActivityItem;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.TransactionExecutor;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
@@ -32,7 +31,6 @@
import android.view.SurfaceControl;
import android.window.ActivityWindowInfo;
import android.window.SplashScreenView.SplashScreenViewParcelable;
-import android.window.WindowContext;
import android.window.WindowContextInfo;
import com.android.internal.annotations.VisibleForTesting;
@@ -90,10 +88,6 @@
/** Get activity instance for the token. */
public abstract Activity getActivity(IBinder token);
- /** Gets the {@link WindowContext} instance for the token. */
- @Nullable
- public abstract Context getWindowContext(@NonNull IBinder clientToken);
-
// Prepare phase related logic and handlers. Methods that inform about about pending changes or
// do other internal bookkeeping.
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 18dc1ce..62a50db 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.servertransaction.ClientTransactionListenerController;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.res.CompatibilityInfo;
@@ -145,6 +146,24 @@
*/
void handleConfigurationChanged(@Nullable Configuration config,
@Nullable CompatibilityInfo compat) {
+ final ClientTransactionListenerController controller =
+ ClientTransactionListenerController.getInstance();
+ final Context contextToUpdate = ActivityThread.currentApplication();
+ controller.onContextConfigurationPreChanged(contextToUpdate);
+ try {
+ handleConfigurationChangedInner(config, compat);
+ } finally {
+ controller.onContextConfigurationPostChanged(contextToUpdate);
+ }
+ }
+
+ /**
+ * Update the configuration to latest.
+ * @param config The new configuration.
+ * @param compat The new compatibility information.
+ */
+ private void handleConfigurationChangedInner(@Nullable Configuration config,
+ @Nullable CompatibilityInfo compat) {
int configDiff;
boolean equivalent;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 8f81ae2..cf06416 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -50,8 +50,8 @@
void cancelAllNotifications(String pkg, int userId);
void clearData(String pkg, int uid, boolean fromApp);
- void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, boolean isUiContext, int displayId, @nullable ITransientNotificationCallback callback);
- void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, boolean isUiContext, int displayId);
+ boolean enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, boolean isUiContext, int displayId, @nullable ITransientNotificationCallback callback);
+ boolean enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, boolean isUiContext, int displayId);
void cancelToast(String pkg, IBinder token);
void finishToken(String pkg, IBinder token);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index fe261be..6ff1bfc5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -23,6 +23,7 @@
import static android.app.admin.DevicePolicyResources.UNDEFINED;
import static android.graphics.drawable.Icon.TYPE_URI;
import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
+import static android.app.Flags.cleanUpSpansAndNewLines;
import static android.app.Flags.evenlyDividedCallStyleActionLayout;
import static android.app.Flags.updateRankingTime;
@@ -2582,6 +2583,7 @@
this.when = System.currentTimeMillis();
if (updateRankingTime()) {
creationTime = when;
+ extras.putBoolean(EXTRA_SHOW_WHEN, true);
} else {
this.creationTime = System.currentTimeMillis();
}
@@ -2597,6 +2599,7 @@
{
if (updateRankingTime()) {
creationTime = when;
+ extras.putBoolean(EXTRA_SHOW_WHEN, true);
}
new Builder(context)
.setWhen(when)
@@ -2629,6 +2632,7 @@
this.when = when;
if (updateRankingTime()) {
creationTime = when;
+ extras.putBoolean(EXTRA_SHOW_WHEN, true);
} else {
this.creationTime = System.currentTimeMillis();
}
@@ -2654,8 +2658,16 @@
if (mAllowlistToken == null) {
mAllowlistToken = processAllowlistToken;
}
- // Propagate this token to all pending intents that are unmarshalled from the parcel.
- parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
+ if (Flags.secureAllowlistToken()) {
+ // Propagate this token to all pending intents that are unmarshalled from the parcel,
+ // or keep the one we're already propagating, if that's the case.
+ if (!parcel.hasClassCookie(PendingIntent.class)) {
+ parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
+ }
+ } else {
+ // Propagate this token to all pending intents that are unmarshalled from the parcel.
+ parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
+ }
when = parcel.readLong();
creationTime = parcel.readLong();
@@ -3124,9 +3136,29 @@
+ " instance is a custom Parcelable and not allowed in Notification");
return cs.toString();
}
+ if (Flags.cleanUpSpansAndNewLines()) {
+ return stripStyling(cs);
+ }
+
return removeTextSizeSpans(cs);
}
+ private static CharSequence stripStyling(@Nullable CharSequence cs) {
+ if (cs == null) {
+ return cs;
+ }
+
+ return cs.toString();
+ }
+
+ private static CharSequence cleanUpNewLines(@Nullable CharSequence charSequence) {
+ if (charSequence == null) {
+ return charSequence;
+ }
+
+ return charSequence.toString().replaceAll("[\r\n]+", "\n");
+ }
+
private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
if (charSequence instanceof Spanned) {
Spanned ss = (Spanned) charSequence;
@@ -3189,9 +3221,29 @@
PendingIntent.addOnMarshaledListener(addedListener);
}
try {
- // IMPORTANT: Add marshaling code in writeToParcelImpl as we
- // want to intercept all pending events written to the parcel.
- writeToParcelImpl(parcel, flags);
+ if (Flags.secureAllowlistToken()) {
+ boolean mustClearCookie = false;
+ if (!parcel.hasClassCookie(Notification.class)) {
+ // This is the "root" notification, and not an "inner" notification (including
+ // publicVersion or anything else that might be embedded in extras).
+ parcel.setClassCookie(Notification.class, this);
+ mustClearCookie = true;
+ }
+ try {
+ // IMPORTANT: Add marshaling code in writeToParcelImpl as we
+ // want to intercept all pending events written to the parcel.
+ writeToParcelImpl(parcel, flags);
+ } finally {
+ if (mustClearCookie) {
+ parcel.removeClassCookie(Notification.class, this);
+ }
+ }
+ } else {
+ // IMPORTANT: Add marshaling code in writeToParcelImpl as we
+ // want to intercept all pending events written to the parcel.
+ writeToParcelImpl(parcel, flags);
+ }
+
synchronized (this) {
// Must be written last!
parcel.writeArraySet(allPendingIntents);
@@ -3206,7 +3258,19 @@
private void writeToParcelImpl(Parcel parcel, int flags) {
parcel.writeInt(1);
- parcel.writeStrongBinder(mAllowlistToken);
+ if (Flags.secureAllowlistToken()) {
+ Notification rootNotification = (Notification) parcel.getClassCookie(
+ Notification.class);
+ if (rootNotification != null && rootNotification != this) {
+ // Always use the same token as the root notification
+ parcel.writeStrongBinder(rootNotification.mAllowlistToken);
+ } else {
+ parcel.writeStrongBinder(mAllowlistToken);
+ }
+ } else {
+ parcel.writeStrongBinder(mAllowlistToken);
+ }
+
parcel.writeLong(when);
parcel.writeLong(creationTime);
if (mSmallIcon == null && icon != 0) {
@@ -3599,18 +3663,23 @@
* Sets the token used for background operations for the pending intents associated with this
* notification.
*
- * This token is automatically set during deserialization for you, you usually won't need to
- * call this unless you want to change the existing token, if any.
+ * Note: Should <em>only</em> be invoked by NotificationManagerService, since this is normally
+ * populated by unparceling (and also used there). Any other usage is suspect.
*
* @hide
*/
- public void clearAllowlistToken() {
- mAllowlistToken = null;
+ public void overrideAllowlistToken(IBinder token) {
+ mAllowlistToken = token;
if (publicVersion != null) {
- publicVersion.clearAllowlistToken();
+ publicVersion.overrideAllowlistToken(token);
}
}
+ /** @hide */
+ public IBinder getAllowlistToken() {
+ return mAllowlistToken;
+ }
+
/**
* @hide
*/
@@ -4316,14 +4385,16 @@
/**
* Add a timestamp pertaining to the notification (usually the time the event occurred).
*
- * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
- * shown anymore by default and must be opted into by using
- * {@link android.app.Notification.Builder#setShowWhen(boolean)}
- *
* @see Notification#when
*/
@NonNull
public Builder setWhen(long when) {
+ if (updateRankingTime()) {
+ // don't show a timestamp that's decades old
+ if (mN.extras.getBoolean(EXTRA_SHOW_WHEN, true) && when == 0) {
+ return this;
+ }
+ }
mN.when = when;
return this;
}
@@ -4331,8 +4402,6 @@
/**
* Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
* in the content view.
- * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
- * {@code false}. For earlier apps, the default is {@code true}.
*/
@NonNull
public Builder setShowWhen(boolean show) {
@@ -5505,7 +5574,8 @@
boolean hasSecondLine = showProgress;
if (p.hasTitle()) {
contentView.setViewVisibility(p.mTitleViewId, View.VISIBLE);
- contentView.setTextViewText(p.mTitleViewId, ensureColorSpanContrast(p.mTitle, p));
+ contentView.setTextViewText(p.mTitleViewId,
+ ensureColorSpanContrastOrStripStyling(p.mTitle, p));
setTextViewColorPrimary(contentView, p.mTitleViewId, p);
} else if (p.mTitleViewId != R.id.title) {
// This alternate title view ID is not cleared by resetStandardTemplate
@@ -5515,7 +5585,8 @@
if (p.mText != null && p.mText.length() != 0
&& (!showProgress || p.mAllowTextWithProgress)) {
contentView.setViewVisibility(p.mTextViewId, View.VISIBLE);
- contentView.setTextViewText(p.mTextViewId, ensureColorSpanContrast(p.mText, p));
+ contentView.setTextViewText(p.mTextViewId,
+ ensureColorSpanContrastOrStripStyling(p.mText, p));
setTextViewColorSecondary(contentView, p.mTextViewId, p);
hasSecondLine = true;
} else if (p.mTextViewId != R.id.text) {
@@ -5804,7 +5875,7 @@
headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
}
if (!TextUtils.isEmpty(headerText)) {
- contentView.setTextViewText(R.id.header_text, ensureColorSpanContrast(
+ contentView.setTextViewText(R.id.header_text, ensureColorSpanContrastOrStripStyling(
processLegacyText(headerText), p));
setTextViewColorSecondary(contentView, R.id.header_text, p);
contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
@@ -5826,8 +5897,9 @@
return false;
}
if (!TextUtils.isEmpty(p.mHeaderTextSecondary)) {
- contentView.setTextViewText(R.id.header_text_secondary, ensureColorSpanContrast(
- processLegacyText(p.mHeaderTextSecondary), p));
+ contentView.setTextViewText(R.id.header_text_secondary,
+ ensureColorSpanContrastOrStripStyling(
+ processLegacyText(p.mHeaderTextSecondary), p));
setTextViewColorSecondary(contentView, R.id.header_text_secondary, p);
contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE);
if (hasTextToLeft) {
@@ -6048,7 +6120,7 @@
big.setViewVisibility(R.id.notification_material_reply_text_1_container,
View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_1,
- ensureColorSpanContrast(replyText[0].getText(), p));
+ ensureColorSpanContrastOrStripStyling(replyText[0].getText(), p));
setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p);
big.setViewVisibility(R.id.notification_material_reply_progress,
showSpinner ? View.VISIBLE : View.GONE);
@@ -6060,7 +6132,7 @@
&& p.maxRemoteInputHistory > 1) {
big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_2,
- ensureColorSpanContrast(replyText[1].getText(), p));
+ ensureColorSpanContrastOrStripStyling(replyText[1].getText(), p));
setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p);
if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2].getText())
@@ -6068,7 +6140,7 @@
big.setViewVisibility(
R.id.notification_material_reply_text_3, View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_3,
- ensureColorSpanContrast(replyText[2].getText(), p));
+ ensureColorSpanContrastOrStripStyling(replyText[2].getText(), p));
setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p);
}
}
@@ -6500,21 +6572,37 @@
mContext, getColors(p).getBackgroundColor(), mInNightMode),
R.dimen.notification_action_disabled_container_alpha);
}
- if (isLegacy()) {
- title = ContrastColorUtil.clearColorSpans(title);
- } else {
- // Check for a full-length span color to use as the button fill color.
- Integer fullLengthColor = getFullLengthSpanColor(title);
- if (fullLengthColor != null) {
- // Ensure the custom button fill has 1.3:1 contrast w/ notification bg.
- int notifBackgroundColor = getColors(p).getBackgroundColor();
- buttonFillColor = ensureButtonFillContrast(
- fullLengthColor, notifBackgroundColor);
+ if (Flags.cleanUpSpansAndNewLines()) {
+ if (!isLegacy()) {
+ // Check for a full-length span color to use as the button fill color.
+ Integer fullLengthColor = getFullLengthSpanColor(title);
+ if (fullLengthColor != null) {
+ // Ensure the custom button fill has 1.3:1 contrast w/ notification bg.
+ int notifBackgroundColor = getColors(p).getBackgroundColor();
+ buttonFillColor = ensureButtonFillContrast(
+ fullLengthColor, notifBackgroundColor);
+ }
}
- // Remove full-length color spans and ensure text contrast with the button fill.
- title = ContrastColorUtil.ensureColorSpanContrast(title, buttonFillColor);
+ } else {
+ if (isLegacy()) {
+ title = ContrastColorUtil.clearColorSpans(title);
+ } else {
+ // Check for a full-length span color to use as the button fill color.
+ Integer fullLengthColor = getFullLengthSpanColor(title);
+ if (fullLengthColor != null) {
+ // Ensure the custom button fill has 1.3:1 contrast w/ notification bg.
+ int notifBackgroundColor = getColors(p).getBackgroundColor();
+ buttonFillColor = ensureButtonFillContrast(
+ fullLengthColor, notifBackgroundColor);
+ }
+ // Remove full-length color spans
+ // and ensure text contrast with the button fill.
+ title = ContrastColorUtil.ensureColorSpanContrast(title, buttonFillColor);
+ }
}
- final CharSequence label = ensureColorSpanContrast(title, p);
+
+
+ final CharSequence label = ensureColorSpanContrastOrStripStyling(title, p);
if (p.mCallStyleActions && evenlyDividedCallStyleActionLayout()) {
if (CallStyle.DEBUG_NEW_ACTION_LAYOUT) {
Log.d(TAG, "new action layout enabled, gluing instead of setting text");
@@ -6554,7 +6642,7 @@
button.setIntDimen(R.id.action0, "setMinimumWidth", minWidthDimen);
}
} else {
- button.setTextViewText(R.id.action0, ensureColorSpanContrast(
+ button.setTextViewText(R.id.action0, ensureColorSpanContrastOrStripStyling(
action.title, p));
button.setTextColor(R.id.action0, getStandardActionColor(p));
}
@@ -6629,6 +6717,26 @@
}
/**
+ * @hide
+ */
+ public CharSequence ensureColorSpanContrastOrStripStyling(CharSequence cs,
+ StandardTemplateParams p) {
+ return ensureColorSpanContrastOrStripStyling(cs, getBackgroundColor(p));
+ }
+
+ /**
+ * @hide
+ */
+ public CharSequence ensureColorSpanContrastOrStripStyling(CharSequence cs,
+ int buttonFillColor) {
+ if (Flags.cleanUpSpansAndNewLines()) {
+ return stripStyling(cs);
+ }
+
+ return ContrastColorUtil.ensureColorSpanContrast(cs, buttonFillColor);
+ }
+
+ /**
* Ensures contrast on color spans against a background color.
* Note that any full-length color spans will be removed instead of being contrasted.
*
@@ -7853,8 +7961,9 @@
RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource(),
p, null /* result */);
if (mSummaryTextSet) {
- contentView.setTextViewText(R.id.text, mBuilder.ensureColorSpanContrast(
- mBuilder.processLegacyText(mSummaryText), p));
+ contentView.setTextViewText(R.id.text,
+ mBuilder.ensureColorSpanContrastOrStripStyling(
+ mBuilder.processLegacyText(mSummaryText), p));
mBuilder.setTextViewColorSecondary(contentView, R.id.text, p);
contentView.setViewVisibility(R.id.text, View.VISIBLE);
}
@@ -8017,6 +8126,9 @@
*/
public BigTextStyle bigText(CharSequence cs) {
mBigText = safeCharSequence(cs);
+ if (Flags.cleanUpSpansAndNewLines()) {
+ mBigText = cleanUpNewLines(mBigText);
+ }
return this;
}
@@ -8517,7 +8629,7 @@
for (int i = 0; i < N; i++) {
final Message m = messages.get(i);
if (ensureContrast) {
- m.ensureColorContrast(backgroundColor);
+ m.ensureColorContrastOrStripStyling(backgroundColor);
}
bundles[i] = m.toBundle();
}
@@ -8543,7 +8655,9 @@
} else {
title = sender;
}
-
+ if (Flags.cleanUpSpansAndNewLines()) {
+ title = stripStyling(title);
+ }
if (title != null) {
extras.putCharSequence(EXTRA_TITLE, title);
}
@@ -8995,6 +9109,17 @@
}
/**
+ * Strip styling or updates TextAppearance spans in message text.
+ * @hide
+ */
+ public void ensureColorContrastOrStripStyling(int backgroundColor) {
+ if (Flags.cleanUpSpansAndNewLines()) {
+ mText = stripStyling(mText);
+ } else {
+ ensureColorContrast(backgroundColor);
+ }
+ }
+ /**
* Updates TextAppearance spans in the message text so it has sufficient contrast
* against its background.
* @hide
@@ -9324,7 +9449,8 @@
if (!TextUtils.isEmpty(str)) {
contentView.setViewVisibility(rowIds[i], View.VISIBLE);
contentView.setTextViewText(rowIds[i],
- mBuilder.ensureColorSpanContrast(mBuilder.processLegacyText(str), p));
+ mBuilder.ensureColorSpanContrastOrStripStyling(
+ mBuilder.processLegacyText(str), p));
mBuilder.setTextViewColorSecondary(contentView, rowIds[i], p);
contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
if (first) {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 02d6944..257aff0 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1603,8 +1603,18 @@
@SetWallpaperFlags int which, boolean originalBitmap) {
checkExactlyOneWallpaperFlagSet(which);
try {
- return sGlobals.mService.getBitmapCrops(displaySizes, which, originalBitmap,
- mContext.getUserId());
+ List<Rect> result = sGlobals.mService.getBitmapCrops(
+ displaySizes, which, originalBitmap, mContext.getUserId());
+ if (result != null) return result;
+ // mService.getBitmapCrops returns null if the requested wallpaper is an ImageWallpaper,
+ // but there are no crop hints and the bitmap size is unknown to the service (this
+ // mostly happens for the default wallpaper). In that case, fetch the bitmap dimensions
+ // and use the other getBitmapCrops API with no cropHints to figure out the crops.
+ Rect bitmapDimensions = peekBitmapDimensions(which, true);
+ if (bitmapDimensions == null) return List.of();
+ Point bitmapSize = new Point(bitmapDimensions.width(), bitmapDimensions.height());
+ return getBitmapCrops(bitmapSize, displaySizes, null);
+
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/AccountTypePolicyKey.java b/core/java/android/app/admin/AccountTypePolicyKey.java
index 51f3137..02e492b 100644
--- a/core/java/android/app/admin/AccountTypePolicyKey.java
+++ b/core/java/android/app/admin/AccountTypePolicyKey.java
@@ -54,7 +54,7 @@
@TestApi
public AccountTypePolicyKey(@NonNull String key, @NonNull String accountType) {
super(key);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
}
mAccountType = Objects.requireNonNull((accountType));
diff --git a/core/java/android/app/admin/BundlePolicyValue.java b/core/java/android/app/admin/BundlePolicyValue.java
index cb5e986..c993671 100644
--- a/core/java/android/app/admin/BundlePolicyValue.java
+++ b/core/java/android/app/admin/BundlePolicyValue.java
@@ -31,7 +31,7 @@
public BundlePolicyValue(Bundle value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxBundleFieldsLength(value);
}
}
diff --git a/core/java/android/app/admin/ComponentNamePolicyValue.java b/core/java/android/app/admin/ComponentNamePolicyValue.java
index a957dbf..a7a2f7d 100644
--- a/core/java/android/app/admin/ComponentNamePolicyValue.java
+++ b/core/java/android/app/admin/ComponentNamePolicyValue.java
@@ -31,7 +31,7 @@
public ComponentNamePolicyValue(@NonNull ComponentName value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxComponentNameLength(value);
}
}
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index f21e11a..c7b0be7 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -244,6 +244,10 @@
* {@link android.app.admin.DevicePolicyManager#isProfileOwnerApp}. You will generally handle
* this in {@link DeviceAdminReceiver#onProfileProvisioningComplete}.
*
+ * <p>The intent for this action may include the following extras:
+ * <ul>
+ * <li>{@link DevicePolicyManager#EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}
+ *
* @see DevicePolicyManager#ACTION_PROVISIONING_SUCCESSFUL
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -801,6 +805,9 @@
* {@link DevicePolicyManager#ACTION_PROVISIONING_SUCCESSFUL} will also be sent to the same
* application.
*
+ * <p>The {@code Intent} may include any of the extras specified for
+ * {@link #ACTION_PROFILE_PROVISIONING_COMPLETE}.
+ *
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
*/
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ea6f45e..9058713 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -54,6 +54,7 @@
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
+import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;
import static android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED;
@@ -191,32 +192,134 @@
import java.util.concurrent.Executor;
import java.util.function.Consumer;
-// TODO(b/172376923) - add CarDevicePolicyManager examples below (or remove reference to it).
/**
- * Public interface for managing policies enforced on a device. Most clients of this class must be
- * registered with the system as a <a href="{@docRoot}guide/topics/admin/device-admin.html">device
- * administrator</a>. Additionally, a device administrator may be registered as either a profile or
- * device owner. A given method is accessible to all device administrators unless the documentation
- * for that method specifies that it is restricted to either device or profile owners. Any
- * application calling an api may only pass as an argument a device administrator component it
- * owns. Otherwise, a {@link SecurityException} will be thrown.
+ * Manages device policy and restrictions applied to the user of the device or
+ * apps running on the device.
*
- * <p><b>Note: </b>on
- * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive builds}, some methods can
- * throw an {@link UnsafeStateException} exception (for example, if the vehicle is moving), so
- * callers running on automotive builds should always check for that exception, otherwise they
- * might crash.
+ * <p>This class contains three types of methods:
+ * <ol><li>Those aimed at <a href="#managingapps">managing apps</a>
+ * <li>Those aimed at the <a href="#roleholder">Device Policy Management Role Holder</a>
+ * <li>Those aimed at <a href="#querying">apps which wish to respect device policy</a>
+ * </ol>
*
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>
- * For more information about managing policies for device administration, read the <a href=
- * "{@docRoot}guide/topics/admin/device-admin.html">Device Administration</a> developer
- * guide. </div>
+ * <p>The intended caller for each API is indicated in its Javadoc.
+ *
+ * <p id="managingapps"><b>Managing Apps</b>
+ * <p>Apps can be made capable of setting device policy ("Managing Apps") either by
+ * being set as a <a href="#deviceadmin">Device Administrator</a>, being set as a
+ * <a href="#devicepolicycontroller">Device Policy Controller</a>, or by holding the
+ * appropriate <a href="#permissions">Permissions</a>.
+ *
+ * <p id="deviceadmin">A <b>Device Administrator</b> is an app which is able to enforce device
+ * policies that it has declared in its device admin XML file. An app can prompt the user to give it
+ * device administator privileges using the {@link #ACTION_ADD_DEVICE_ADMIN} action.
+ *
+ * <p>For more information about Device Administration, read the
+ * <a href="{@docRoot}guide/topics/admin/device-admin.html">Device Administration</a>
+ * developer guide.
+ *
+ * <p id="devicepolicycontroller">Through <a href="#managed_provisioning">Managed Provisioning</a>,
+ * Device Administrator apps can also be recognised as <b>
+ Device Policy Controllers</b>. Device Policy Controllers can be one of
+ * two types:
+ * <ul>
+ * <li>A <i id="deviceowner">Device Owner</i>, which only ever exists on the
+ * {@link UserManager#isSystemUser System User} or {@link UserManager#isMainUser Main User}, is
+ * the most powerful type of Device Policy Controller and can affect policy across the device.
+ * <li>A <i id="profileowner">Profile Owner<i>, which can exist on any user, can
+ * affect policy on the user it is on, and when it is running on
+ * {@link UserManager#isProfile a profile} has
+ * <a href="#profile-on-parent">limited</a> ability to affect policy on its
+ * {@link UserManager#getProfileParent parent}.
+ * </ul>
+ *
+ * <p>Additional capabilities can be provided to Device Policy Controllers in
+ * the following circumstances:
+ * <ul>
+ * <li>A Profile Owner on an <a href="#organization-owned">organization owned</a> device has access
+ * to additional abilities, both <a href="#profile-on-parent-organization-owned">affecting policy on the profile's</a>
+ * {@link UserManager#getProfileParent parent} and also the profile itself.
+ * <li>A Profile Owner running on the {@link UserManager#isSystemUser System User} has access to
+ * additional capabilities which affect the {@link UserManager#isSystemUser System User} and
+ * also the whole device.
+ * <li>A Profile Owner running on an <a href="#affiliated">affiliated</a> user has
+ * capabilities similar to that of a <a href="#deviceowner">Device Owner</a>
+ * </ul>
+ *
+ * <p>For more information, see <a href="{@docRoot}work/dpc/build-dpc">Building a Device Policy
+ * Controller</a>.
+ *
+ * <p><a href="#permissions">Permissions</a> are generally only given to apps
+ * fulfilling particular key roles on the device (such as managing {@link DeviceLockManager
+device locks}).
+ *
+ * <p id="roleholder"><b>Device Policy Management Role Holder</b>
+ * <p>One app on the device fulfills the {@link RoleManager#ROLE_DEVICE_POLICY_MANAGEMENT Device
+Policy Management Role} and is trusted with managing the overall state of
+ * Device Policy. This has access to much more powerful methods than
+ * <a href="#managingapps">managing apps</a>.
+ *
+ * <p id="querying"><b>Querying Device Policy</b>
+ * <p>In most cases, regular apps do not need to concern themselves with device
+ * policy, and restrictions will be enforced automatically. There are some cases
+ * where an app may wish to query device policy to provide a better user
+ * experience. Only a small number of policies allow apps to query them directly.
+ * These APIs will typically have no special required permissions.
+ *
+ * <p id="managedprovisioning"><b>Managed Provisioning</b>
+ * <p>Managed Provisioning is the process of recognising an app as a
+ * <a href="#deviceowner">Device Owner</a> or <a href="#profileowner">Profile Owner</a>. It
+ * involves presenting education and consent screens to the user to ensure they
+ * are aware of the capabilities this grants the <a href="#devicepolicycontroller">Device Policy
+ * Controller</a>
+ *
+ * <p>For more information on provisioning, see <a href="{@docRoot}work/dpc/build-dpc">Building a
+ * Device Policy Controller</a>.
+ *
+ * <p id="managed_profile">A <b>Managed Profile</b> enables data separation. For example to use
+ * a device both for personal and corporate usage. The managed profile and its
+ * {@link UserManager#getProfileParent parent} share a launcher.
+ *
+ * <p id="affiliated"><b>Affiliation</b>
+ * <p>Using the {@link #setAffiliationIds} method, a
+ * <a href="#deviceowner">Device Owner</a> can set a list of affiliation ids for the
+ * {@link UserManager#isSystemUser System User}. Any <a href="#profileowner">Profile Owner</a> on
+ * the same device can also call {@link #setAffiliationIds} to set affiliation ids
+ * for the {@link UserManager user} it is on. When there is the same ID
+ * present in both lists, the user is said to be "affiliated" and we can refer to
+ * the <a href="#profileowner">Profile Owner</a> as a "profile owner on an affiliated
+ * user" or an "affiliated profile owner".
+ *
+ * Becoming affiliated grants the <a href="#profileowner">Profile Owner</a> capabilities similar to
+ * that of the <a href="#deviceowner">Device Owner</a>. It also allows use of the
+ * {@link #bindDeviceAdminServiceAsUser} APIs for direct communication between the
+ * <a href="#deviceowner">Device Owner</a> and
+ * affiliated <a href="#profileowner">Profile Owners</a>.
+ *
+ * <p id="organization-owned"><b>Organization Owned</b></p>
+ * An organization owned device is one which is not owned by the person making use of the device and
+ * is instead owned by an organization such as their employer or education provider. These devices
+ * are recognised as being organization owned either by the presence of a
+ * <a href="#deviceowner">device owner</a> or of a
+ * {@link #isOrganizationOwnedDeviceWithManagedProfile profile which has a profile owner is marked
+ * as organization owned}.
+ *
+ * <p id="profile-on-parent-organization-owned">Profile owners running on an
+ * <a href="organization-owned">organization owned</a> device can exercise additional capabilities
+ * using the {@link #getParentProfileInstance(ComponentName)} API which apply to the parent user.
+ * Each API will indicate if it is usable in this way.
+ *
+ * <p id="automotive"><b>Android Automotive</b>
+ * <p>On {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE
+ * "Android Automotive builds"}, some methods can throw
+ * {@link UnsafeStateException "an exception"} if an action is unsafe (for example, if the vehicle
+ * is moving). Callers running on
+ * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE
+ * "Android Automotive builds"} should always check for this exception.
*/
+
@SystemService(Context.DEVICE_POLICY_SERVICE)
@RequiresFeature(PackageManager.FEATURE_DEVICE_ADMIN)
-@SuppressLint("UseIcu")
public class DevicePolicyManager {
/** @hide */
@@ -256,7 +359,7 @@
* Fetch the current value of mService. This is used in the binder cache lambda
* expressions.
*/
- private final IDevicePolicyManager getService() {
+ private IDevicePolicyManager getService() {
return mService;
}
@@ -264,7 +367,7 @@
* Fetch the current value of mParentInstance. This is used in the binder cache
* lambda expressions.
*/
- private final boolean isParentInstance() {
+ private boolean isParentInstance() {
return mParentInstance;
}
@@ -272,7 +375,7 @@
* Fetch the current value of mContext. This is used in the binder cache lambda
* expressions.
*/
- private final Context getContext() {
+ private Context getContext() {
return mContext;
}
@@ -283,39 +386,80 @@
}
/**
- * Activity action: Starts the provisioning flow which sets up a managed profile.
- *
- * <p>A managed profile allows data separation for example for the usage of a
- * device as a personal and corporate device. The user which provisioning is started from and
- * the managed profile share a launcher.
- *
- * <p>This intent will typically be sent by a mobile device management application (MDM).
- * Provisioning adds a managed profile and sets the MDM as the profile owner who has full
- * control over the profile.
+ * Activity action: Starts the provisioning flow which sets up a
+ * <a href="#managed-profile">managed profile</a>.
*
* <p>It is possible to check if provisioning is allowed or not by querying the method
* {@link #isProvisioningAllowed(String)}.
*
- * <p>In version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this intent must contain the
- * extra {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}.
- * As of {@link android.os.Build.VERSION_CODES#M}, it should contain the extra
- * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} instead, although specifying only
- * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} is still supported.
+ * <p>The intent may contain the following extras:
*
- * <p>The intent may also contain the following extras:
- * <ul>
- * <li>{@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE}, optional </li>
- * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional, supported from
- * {@link android.os.Build.VERSION_CODES#N}</li>
- * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_SKIP_USER_CONSENT}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_DISCLAIMERS}, optional</li>
- * </ul>
+ * <table>
+ * <thead>
+ * <tr>
+ * <th>Extra</th>
+ * <th></th>
+ * <th>Supported Versions</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>{@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE}</td>
+ * <td colspan="2"></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}</td>
+ * <td></td>
+ * <td>{@link android.os.Build.VERSION_CODES#N}+</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}</td>
+ * <td colspan="2"></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXTRA_PROVISIONING_LOGO_URI}</td>
+ * <td colspan="2"></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXTRA_PROVISIONING_SKIP_USER_CONSENT}</td>
+ * <td colspan="2"><b>Can only be used by an existing device owner trying to create a
+ * managed profile</b></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION}</td>
+ * <td colspan="2"></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXTRA_PROVISIONING_DISCLAIMERS}</td>
+ * <td colspan="2"></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}</td>
+ * <td>
+ * <b>Required if {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} is not
+ * specified. Must match the package name of the calling application.</b>
+ * </td>
+ * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}+</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}</td>
+ * <td>
+ * <b>Required if {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} is not
+ * specified. Package name must match the package name of the calling
+ * application.</b>
+ * </td>
+ * <td>{@link android.os.Build.VERSION_CODES#M}+</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXTRA_PROVISIONING_ALLOW_OFFLINE}</td>
+ * <td colspan="2">On {@link android.os.Build.VERSION_CODES#TIRAMISU}+, when set to
+ * true this will <b>force</b> offline provisioning instead of allowing it</td>
+ * </tr>
+ * </tbody>
+ * </table>
*
- * <p>When managed provisioning has completed, broadcasts are sent to the application specified
- * in the provisioning intent. The
+ * <p>When <a href="#managedprovisioning">managed provisioning</a> has completed, broadcasts
+ * are sent to the application specified in the provisioning intent. The
* {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} broadcast is sent in the
* managed profile and the {@link #ACTION_MANAGED_PROFILE_PROVISIONED} broadcast is sent in
* the primary profile.
@@ -324,25 +468,25 @@
* completed, along with the above broadcast, activity intent
* {@link #ACTION_PROVISIONING_SUCCESSFUL} will also be sent to the profile owner.
*
- * <p>If provisioning fails, the managedProfile is removed so the device returns to its
+ * <p>If provisioning fails, the managed profile is removed so the device returns to its
* previous state.
*
* <p>If launched with {@link android.app.Activity#startActivityForResult(Intent, int)} a
- * result code of {@link android.app.Activity#RESULT_OK} implies that the synchronous part of
+ * result code of {@link android.app.Activity#RESULT_OK} indicates that the synchronous part of
* the provisioning flow was successful, although this doesn't guarantee the full flow will
- * succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} implies
- * that the user backed-out of provisioning, or some precondition for provisioning wasn't met.
+ * succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} indicates
+ * that the user backed-out of provisioning or some precondition for provisioning wasn't met.
*
- * <p>If a device policy management role holder (DPMRH) updater is present on the device, an
- * internet connection attempt must be made prior to launching this intent. If internet
- * connection could not be established, provisioning will fail unless {@link
+ * <p>If a <a href="#roleholder">device policy management role holder</a> updater is present on
+ * the device, an internet connection attempt must be made prior to launching this intent. If
+ * an internet connection can not be established, provisioning will fail unless {@link
* #EXTRA_PROVISIONING_ALLOW_OFFLINE} is explicitly set to {@code true}, in which case
- * provisioning will continue without using the DPMRH. If an internet connection has been
- * established, the DPMRH updater will be launched, which will update the DPMRH if it's not
- * present on the device, or if it's present and not valid.
- *
- * <p>If a DPMRH is present on the device and valid, the provisioning flow will be deferred to
- * it.
+ * provisioning will continue without using the
+ * <a href="#roleholder">device policy management role holder</a>. If an internet connection
+ * has been established, the <a href="#roleholder">device policy management role holder</a>
+ * updater will be launched, which may update the
+ * <a href="#roleholder">device policy management role holder</a> before continuing
+ * provisioning.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_PROVISION_MANAGED_PROFILE
@@ -821,31 +965,25 @@
"android.app.extra.FORCE_UPDATE_ROLE_HOLDER";
/**
- * A boolean extra indicating whether offline provisioning is allowed.
- *
- * <p>For the online provisioning flow, there will be an attempt to download and install
- * the latest version of the device policy management role holder. The platform will then
- * delegate provisioning to the device policy management role holder via role holder-specific
- * provisioning actions.
- *
- * <p>For the offline provisioning flow, the provisioning flow will always be handled by
- * the platform.
- *
- * <p>If this extra is set to {@code false}, the provisioning flow will enforce that an
- * internet connection is established, which will start the online provisioning flow. If an
- * internet connection cannot be established, provisioning will fail.
- *
- * <p>If this extra is set to {@code true}, the provisioning flow will still try to connect to
- * the internet, but if it fails it will start the offline provisioning flow.
- *
- * <p>For T if this extra is set to {@code true}, the provisioning flow will be forced through
- * the platform and there will be no attempt to download and install the device policy
- * management role holder.
+ * A boolean extra indicating whether offline provisioning should be used.
*
* <p>The default value is {@code false}.
*
- * <p>This extra is respected when provided via the provisioning intent actions such as {@link
- * #ACTION_PROVISION_MANAGED_PROFILE}.
+ * <p>Usually during the <a href="#managedprovisioning">provisioning flow</a>, there will be
+ * an attempt to download and install the latest version of the <a href="#roleholder">device
+ * policy management role holder</a>. The platform will then
+ * delegate provisioning to the <a href="#roleholder">device
+ * * policy management role holder</a>.
+ *
+ * <p>When this extra is set to {@code true}, the
+ * <a href="#managedprovisioning">provisioning flow</a> will always be handled by the platform
+ * and the <a href="#roleholder">device policy management role holder</a>'s part skipped.
+ *
+ * <p>On Android versions prior to {@link Build.VERSION_CODES#TIRAMISU}, when this extra is
+ * {@code false}, the <a href="#managedprovisioning">provisioning flow</a> will enforce that an
+ * internet connection is established, or otherwise fail. When this extra is {@code true}, a
+ * connection will still be attempted but when it cannot be established provisioning will
+ * continue offline.
*/
public static final String EXTRA_PROVISIONING_ALLOW_OFFLINE =
"android.app.extra.PROVISIONING_ALLOW_OFFLINE";
@@ -1056,64 +1194,40 @@
public static final long DEFAULT_STRONG_AUTH_TIMEOUT_MS = 72 * 60 * 60 * 1000; // 72h
/**
- * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
- * allows a mobile device management application or NFC programmer application which starts
- * managed provisioning to pass data to the management application instance after provisioning.
+ * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that is
+ * passed directly to the <a href="#devicepolicycontroller">Device Policy Controller</a>
+ * after <a href="#managed-provisioning">provisioning</a>.
+ *
* <p>
- * If used with {@link #ACTION_PROVISION_MANAGED_PROFILE} it can be used by the application that
- * sends the intent to pass data to itself on the newly created profile.
- * If used with {@link #ACTION_PROVISION_MANAGED_DEVICE} it allows passing data to the same
- * instance of the app on the primary user.
* Starting from {@link android.os.Build.VERSION_CODES#M}, if used with
* {@link #MIME_TYPE_PROVISIONING_NFC} as part of NFC managed device provisioning, the NFC
* message should contain a stringified {@link java.util.Properties} instance, whose string
* properties will be converted into a {@link android.os.PersistableBundle} and passed to the
* management application after provisioning.
- *
- * <p>Admin apps will receive this extra in their {@link #ACTION_GET_PROVISIONING_MODE} and
- * {@link #ACTION_ADMIN_POLICY_COMPLIANCE} intent handlers. Additionally, {@link
- * #ACTION_GET_PROVISIONING_MODE} may also return this extra which will then be sent over to
- * {@link #ACTION_ADMIN_POLICY_COMPLIANCE}, alongside the original values that were passed to
- * {@link #ACTION_GET_PROVISIONING_MODE}.
- *
- * <p>
- * In both cases the application receives the data in
- * {@link DeviceAdminReceiver#onProfileProvisioningComplete} via an intent with the action
- * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE}. The bundle is not changed
- * during the managed provisioning.
*/
public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE =
"android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
/**
- * A String extra holding the package name of the mobile device management application that
- * will be set as the profile owner or device owner.
+ * A String extra holding the package name of the application that
+ * will be set as <a href="#devicepolicycontroller">Device Policy Controller</a>.
*
- * <p>If an application starts provisioning directly via an intent with action
- * {@link #ACTION_PROVISION_MANAGED_PROFILE} this package has to match the package name of the
- * application that started provisioning. The package will be set as profile owner in that case.
+ * <p>When this extra is set, the application must have exactly one
+ * {@link DeviceAdminReceiver device admin receiver}. This receiver will be set as the
+ * <a href="#devicepolicycontroller">Device Policy Controller</a>.
*
- * <p>This package is set as device owner when device owner provisioning is started by an NFC
- * message containing an NFC record with MIME type {@link #MIME_TYPE_PROVISIONING_NFC}.
- *
- * <p> When this extra is set, the application must have exactly one device admin receiver.
- * This receiver will be set as the profile or device owner and active admin.
- *
- * @see DeviceAdminReceiver
- * @deprecated Use {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}. This extra is still
- * supported, but only if there is only one device admin receiver in the package that requires
- * the permission {@link android.Manifest.permission#BIND_DEVICE_ADMIN}.
+ * @deprecated Use {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}.
*/
@Deprecated
public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME
= "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME";
/**
- * A ComponentName extra indicating the device admin receiver of the mobile device management
- * application that will be set as the profile owner or device owner and active admin.
+ * A ComponentName extra indicating the {@link DeviceAdminReceiver device admin receiver} of
+ * the application that will be set as the <a href="#devicepolicycontroller">
+ * Device Policy Controller</a>.
*
* <p>If an application starts provisioning directly via an intent with action
- * {@link #ACTION_PROVISION_MANAGED_PROFILE} or
* {@link #ACTION_PROVISION_MANAGED_DEVICE} the package name of this
* component has to match the package name of the application that started provisioning.
*
@@ -1122,35 +1236,28 @@
* message containing an NFC record with MIME type
* {@link #MIME_TYPE_PROVISIONING_NFC}. For the NFC record, the component name must be
* flattened to a string, via {@link ComponentName#flattenToShortString()}.
- *
- * @see DeviceAdminReceiver
*/
public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME
= "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME";
/**
* An {@link android.accounts.Account} extra holding the account to migrate during managed
- * profile provisioning. If the account supplied is present in the primary user, it will be
- * copied, along with its credentials to the managed profile and removed from the primary user.
+ * profile provisioning.
*
- * Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}, with managed account provisioning, or
- * return as an extra to the intent result from the {@link #ACTION_GET_PROVISIONING_MODE}
- * activity.
+ * <p>If the account supplied is present in the user, it will be copied, along with its
+ * credentials to the managed profile and removed from the user.
*/
-
public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE
= "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
/**
- * Boolean extra to indicate that the migrated account should be kept. This is used in
- * conjunction with {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE}. If it's set to {@code true},
- * the account will not be removed from the primary user after it is migrated to the newly
- * created user or profile.
+ * Boolean extra to indicate that the
+ * {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE migrated account} should be kept.
*
- * <p> Defaults to {@code false}
+ * <p>If it's set to {@code true}, the account will not be removed from the user after it is
+ * migrated to the newly created user or profile.
*
- * <p> Use with {@link #ACTION_PROVISION_MANAGED_PROFILE} or set as an extra to the
- * intent result of the {@link #ACTION_GET_PROVISIONING_MODE} activity.
+ * <p>Defaults to {@code false}
*
* @see #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE
*/
@@ -1589,17 +1696,14 @@
"android.app.action.PROVISIONING_SUCCESSFUL";
/**
- * A boolean extra indicating whether device encryption can be skipped as part of device owner
- * or managed profile provisioning.
+ * A boolean extra indicating whether device encryption can be skipped as part of
+ * <a href="#managed-provisioning>provisioning</a>.
*
* <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} or an intent with action
* {@link #ACTION_PROVISION_MANAGED_DEVICE} that starts device owner provisioning.
*
* <p>From {@link android.os.Build.VERSION_CODES#N} onwards, this is also supported for an
* intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE}.
- *
- * <p>This extra can also be returned by the admin app when performing the admin-integrated
- * provisioning flow as a result of the {@link #ACTION_GET_PROVISIONING_MODE} activity.
*/
public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION =
"android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
@@ -1607,23 +1711,22 @@
/**
* A {@link Uri} extra pointing to a logo image. This image will be shown during the
* provisioning. If this extra is not passed, a default image will be shown.
- * <h5>The following URI schemes are accepted:</h5>
+ *
+ * <p><b>The following URI schemes are accepted:</b>
* <ul>
* <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
* <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
* </ul>
*
- * <p> It is the responsibility of the caller to provide an image with a reasonable
+ * <p>It is the responsibility of the caller to provide an image with a reasonable
* pixel density for the device.
*
- * <p> If a content: URI is passed, the intent should have the flag
+ * <p>If a content: URI is passed, the intent should also have the flag
* {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and the uri should be added to the
- * {@link android.content.ClipData} of the intent too.
+ * {@link android.content.ClipData} of the intent.
*
- * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE} or
- * {@link #ACTION_PROVISION_MANAGED_DEVICE}
- *
- * @deprecated Logo customization is no longer supported in the provisioning flow.
+ * @deprecated Logo customization is no longer supported in the
+ * <a href="#managedprovisioning">provisioning flow</a>.
*/
@Deprecated
public static final String EXTRA_PROVISIONING_LOGO_URI =
@@ -1631,7 +1734,8 @@
/**
* A {@link Bundle}[] extra consisting of list of disclaimer headers and disclaimer contents.
- * Each {@link Bundle} must have both {@link #EXTRA_PROVISIONING_DISCLAIMER_HEADER}
+ *
+ * <p>Each {@link Bundle} must have both {@link #EXTRA_PROVISIONING_DISCLAIMER_HEADER}
* as disclaimer header, and {@link #EXTRA_PROVISIONING_DISCLAIMER_CONTENT} as disclaimer
* content.
*
@@ -1652,20 +1756,21 @@
/**
* A String extra of localized disclaimer header.
*
- * <p> The extra is typically the company name of mobile device management application (MDM)
+ * <p>The extra is typically the company name of mobile device management application (MDM)
* or the organization name.
*
- * <p> Use in Bundle {@link #EXTRA_PROVISIONING_DISCLAIMERS}
+ * <p>{@link ApplicationInfo#FLAG_SYSTEM System apps} can also insert a disclaimer by declaring
+ * an application-level meta-data in {@code AndroidManifest.xml}.
*
- * <p> System app, i.e. application with {@link ApplicationInfo#FLAG_SYSTEM}, can also insert a
- * disclaimer by declaring an application-level meta-data in {@code AndroidManifest.xml}.
- * Must use it with {@link #EXTRA_PROVISIONING_DISCLAIMER_CONTENT}. Here is the example:
- *
+ * <p>For example:
* <pre>
* <meta-data
* android:name="android.app.extra.PROVISIONING_DISCLAIMER_HEADER"
* android:resource="@string/disclaimer_header"
* /></pre>
+ *
+ * <p>This must be accompanied with another extra using the key
+ * {@link #EXTRA_PROVISIONING_DISCLAIMER_CONTENT}.
*/
public static final String EXTRA_PROVISIONING_DISCLAIMER_HEADER =
"android.app.extra.PROVISIONING_DISCLAIMER_HEADER";
@@ -1679,35 +1784,35 @@
* <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
* </ul>
*
- * <p> Styled text is supported in the disclaimer content. The content is parsed by
- * {@link android.text.Html#fromHtml(String)} and displayed in a
- * {@link android.widget.TextView}.
+ * <p>Styled text is supported. This is parsed by {@link android.text.Html#fromHtml(String)}
+ * and displayed in a {@link android.widget.TextView}.
*
- * <p> If a <code>content:</code> URI is passed, URI is passed, the intent should have the flag
- * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and the uri should be added to the
- * {@link android.content.ClipData} of the intent too.
+ * <p>If a <code>content:</code> URI is passed, the intent should also have the
+ * flag {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and the uri should be added to the
+ * {@link android.content.ClipData} of the intent.
*
- * <p> Use in Bundle {@link #EXTRA_PROVISIONING_DISCLAIMERS}
- *
- * <p> System app, i.e. application with {@link ApplicationInfo#FLAG_SYSTEM}, can also insert a
+ * <p>{@link ApplicationInfo#FLAG_SYSTEM System apps} can also insert a
* disclaimer by declaring an application-level meta-data in {@code AndroidManifest.xml}.
- * Must use it with {@link #EXTRA_PROVISIONING_DISCLAIMER_HEADER}. Here is the example:
+ *
+ * <p>For example:
*
* <pre>
* <meta-data
* android:name="android.app.extra.PROVISIONING_DISCLAIMER_CONTENT"
* android:resource="@string/disclaimer_content"
* /></pre>
+ *
+ * <p>This must be accompanied with another extra using the key
+ * {@link #EXTRA_PROVISIONING_DISCLAIMER_HEADER}.
*/
public static final String EXTRA_PROVISIONING_DISCLAIMER_CONTENT =
"android.app.extra.PROVISIONING_DISCLAIMER_CONTENT";
/**
- * A boolean extra indicating if the user consent steps from the provisioning flow should be
- * skipped. If unspecified, defaults to {@code false}.
+ * A boolean extra indicating if the user consent steps from the
+ * <a href="#managed-provisioning">provisioning flow</a> should be skipped.
*
- * It can only be used by an existing device owner trying to create a managed profile via
- * {@link #ACTION_PROVISION_MANAGED_PROFILE}. Otherwise it is ignored.
+ * <p>If unspecified, defaults to {@code false}.
*
* @deprecated this extra is no longer relevant as device owners cannot create managed profiles
*/
@@ -7055,9 +7160,10 @@
public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
/**
- * Disable all keyguard widgets. Has no effect starting from
- * {@link android.os.Build.VERSION_CODES#LOLLIPOP} since keyguard widget is only supported
- * on Android versions lower than 5.0.
+ * Disable all keyguard widgets. Has no effect between {@link
+ * android.os.Build.VERSION_CODES#LOLLIPOP} and {@link
+ * android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} (both inclusive), since keyguard widget is
+ * only supported on Android versions lower than 5.0 and versions higher than 14.
*/
public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1 << 0;
@@ -7156,7 +7262,8 @@
public static final int ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY =
DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
| DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS
- | DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL;
+ | DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
+ | DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL;
/**
* Keyguard features that when set on a normal or organization-owned managed profile, have
@@ -8977,6 +9084,10 @@
* by applications in the managed profile.
* </ul>
* <p>
+ * From version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, the profile owner of a
+ * managed profile can also set {@link #KEYGUARD_DISABLE_WIDGETS_ALL} which disables keyguard
+ * widgets for the managed profile.
+ * <p>
* From version {@link android.os.Build.VERSION_CODES#R} the profile owner of an
* organization-owned managed profile can set:
* <ul>
@@ -8985,6 +9096,12 @@
* <li>{@link #KEYGUARD_DISABLE_SECURE_NOTIFICATIONS} which affects the parent user when called
* on the parent profile.
* </ul>
+ * Starting from version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} the profile
+ * owner of an organization-owned managed profile can set:
+ * <ul>
+ * <li>{@link #KEYGUARD_DISABLE_WIDGETS_ALL} which affects the parent user when called on the
+ * parent profile.
+ * </ul>
* {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, {@link #KEYGUARD_DISABLE_FINGERPRINT},
* {@link #KEYGUARD_DISABLE_FACE}, {@link #KEYGUARD_DISABLE_IRIS},
* {@link #KEYGUARD_DISABLE_SECURE_CAMERA} and {@link #KEYGUARD_DISABLE_SECURE_NOTIFICATIONS}
@@ -17560,6 +17677,48 @@
}
/**
+ * Force sets the maximum storage size allowed for policies associated with an admin regardless
+ * of the default value set in the system, unlike {@link #setMaxPolicyStorageLimit} which can
+ * only set it to a value higher than the default value set by the system.Setting a limit of -1
+ * effectively removes any storage restrictions.
+ *
+ * @param storageLimit Maximum storage allowed in bytes. Use -1 to disable limits.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT)
+ @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
+ public void forceSetMaxPolicyStorageLimit(int storageLimit) {
+ if (mService != null) {
+ try {
+ mService.forceSetMaxPolicyStorageLimit(mContext.getPackageName(), storageLimit);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Retrieves the size of the current policies set by the {@code admin}.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT)
+ @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
+ public int getPolicySizeForAdmin(@NonNull EnforcingAdmin admin) {
+ if (mService != null) {
+ try {
+ return mService.getPolicySizeForAdmin(mContext.getPackageName(), admin);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return -1;
+ }
+
+ /**
* @return The headless device owner mode for the current set DO, returns
* {@link DeviceAdminInfo#HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED} if no DO is set.
*
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 1aee9fe..a9f2d74 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -317,11 +317,6 @@
public abstract boolean isUserOrganizationManaged(@UserIdInt int userId);
/**
- * Returns whether the application exemptions feature flag is enabled.
- */
- public abstract boolean isApplicationExemptionsFlagEnabled();
-
- /**
* Returns a map of admin to {@link Bundle} map of restrictions set by the admins for the
* provided {@code packageName} in the provided {@code userId}
*/
diff --git a/core/java/android/app/admin/EnforcingAdmin.java b/core/java/android/app/admin/EnforcingAdmin.java
index 7c718f6..f70a53f 100644
--- a/core/java/android/app/admin/EnforcingAdmin.java
+++ b/core/java/android/app/admin/EnforcingAdmin.java
@@ -16,9 +16,13 @@
package android.app.admin;
+import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -60,6 +64,8 @@
*
* @hide
*/
+ @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
+ @TestApi
public EnforcingAdmin(
@NonNull String packageName, @NonNull Authority authority,
@NonNull UserHandle userHandle, @Nullable ComponentName componentName) {
@@ -101,6 +107,16 @@
return mUserHandle;
}
+ /**
+ * Returns the {@link ComponentName} of the admin if applicable.
+ *
+ * @hide
+ */
+ @Nullable
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 2002326..d183713 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -623,8 +623,10 @@
int[] getSubscriptionIds(String callerPackageName);
- void setMaxPolicyStorageLimit(String packageName, int storageLimit);
- int getMaxPolicyStorageLimit(String packageName);
+ void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit);
+ void forceSetMaxPolicyStorageLimit(String callerPackageName, int storageLimit);
+ int getMaxPolicyStorageLimit(String callerPackageName);
+ int getPolicySizeForAdmin(String callerPackageName, in EnforcingAdmin admin);
int getHeadlessDeviceOwnerMode(String callerPackageName);
}
diff --git a/core/java/android/app/admin/LockTaskPolicy.java b/core/java/android/app/admin/LockTaskPolicy.java
index a36ea05..68b4ad8 100644
--- a/core/java/android/app/admin/LockTaskPolicy.java
+++ b/core/java/android/app/admin/LockTaskPolicy.java
@@ -135,7 +135,7 @@
}
private void setPackagesInternal(Set<String> packages) {
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
for (String p : packages) {
PolicySizeVerifier.enforceMaxPackageNameLength(p);
}
diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java
index 389585f..1a04f6c 100644
--- a/core/java/android/app/admin/PackagePermissionPolicyKey.java
+++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java
@@ -59,7 +59,7 @@
public PackagePermissionPolicyKey(@NonNull String identifier, @NonNull String packageName,
@NonNull String permissionName) {
super(identifier);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
}
diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java
index 68dc797..9e31a23 100644
--- a/core/java/android/app/admin/PackagePolicyKey.java
+++ b/core/java/android/app/admin/PackagePolicyKey.java
@@ -55,7 +55,7 @@
@TestApi
public PackagePolicyKey(@NonNull String key, @NonNull String packageName) {
super(key);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
}
mPackageName = Objects.requireNonNull((packageName));
diff --git a/core/java/android/app/admin/Provisioning_OWNERS b/core/java/android/app/admin/Provisioning_OWNERS
index 8f71fc0..91b9761 100644
--- a/core/java/android/app/admin/Provisioning_OWNERS
+++ b/core/java/android/app/admin/Provisioning_OWNERS
@@ -1,4 +1,4 @@
# Assign bugs to android-enterprise-triage@google.com
ae-provisioning-reviews@google.com
-petuska@google.com #{LAST_RESORT_SUGGESTION}
+acjohnston@google.com #{LAST_RESORT_SUGGESTION}
file:EnterprisePlatform_OWNERS
diff --git a/core/java/android/app/admin/StringPolicyValue.java b/core/java/android/app/admin/StringPolicyValue.java
index 8995c0f..6efe9ad 100644
--- a/core/java/android/app/admin/StringPolicyValue.java
+++ b/core/java/android/app/admin/StringPolicyValue.java
@@ -30,7 +30,7 @@
public StringPolicyValue(@NonNull String value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
}
}
diff --git a/core/java/android/app/admin/StringSetPolicyValue.java b/core/java/android/app/admin/StringSetPolicyValue.java
index f37dfee..12b11f4 100644
--- a/core/java/android/app/admin/StringSetPolicyValue.java
+++ b/core/java/android/app/admin/StringSetPolicyValue.java
@@ -32,7 +32,7 @@
public StringSetPolicyValue(@NonNull Set<String> value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
for (String str : value) {
PolicySizeVerifier.enforceMaxStringLength(str, "policyValue");
}
diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java
index ee90ccd..9054287 100644
--- a/core/java/android/app/admin/UserRestrictionPolicyKey.java
+++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java
@@ -45,7 +45,7 @@
@TestApi
public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) {
super(identifier);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
}
mRestriction = Objects.requireNonNull(restriction);
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index fe2f764..ac843cb 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -27,6 +27,17 @@
}
flag {
+ name: "device_policy_size_tracking_internal_bug_fix_enabled"
+ namespace: "enterprise"
+ description: "Bug fix for tracking the total policy size and have a max threshold"
+ bug: "281543351"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+
+flag {
name: "onboarding_bugreport_v2_enabled"
is_exported: true
namespace: "enterprise"
@@ -184,6 +195,25 @@
}
}
+flag {
+ name: "power_exemption_bg_usage_fix"
+ namespace: "enterprise"
+ description: "Ensure aps with EXEMPT_FROM_POWER_RESTRICTIONS can execute in the background"
+ bug: "333379020"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "disallow_user_control_bg_usage_fix"
+ namespace: "enterprise"
+ description: "Make DPM.setUserControlDisabledPackages() ensure background usage is allowed"
+ bug: "326031059"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
flag {
name: "esim_management_ux_enabled"
@@ -217,6 +247,16 @@
}
flag {
+ name: "always_persist_do"
+ namespace: "enterprise"
+ description: "Always write device_owners2.xml so that migration flags aren't lost"
+ bug: "335232744"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "is_recursive_required_app_merging_enabled"
namespace: "enterprise"
description: "Guards a new flow for recursive required enterprise app list merging"
@@ -232,3 +272,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "headless_single_user_bad_device_admin_state_fix"
+ namespace: "enterprise"
+ description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change"
+ bug: "332477138"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 0214d40..250953e 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -74,6 +74,16 @@
}
flag {
+ name: "secure_allowlist_token"
+ namespace: "systemui"
+ description: "Prevents allowlist_token from leaking out and foreign tokens from being accepted"
+ bug: "328254922"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "update_ranking_time"
namespace: "systemui"
description: "Updates notification sorting criteria to highlight new content while maintaining stability"
@@ -109,4 +119,11 @@
namespace: "systemui"
description: "No notifs can use USAGE_UNKNOWN or USAGE_MEDIA"
bug: "331793339"
+}
+
+flag {
+ name: "clean_up_spans_and_new_lines"
+ namespace: "systemui"
+ description: "Cleans up spans and unnecessary new lines from standard notification templates"
+ bug: "313439845"
}
\ No newline at end of file
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 6317725..11d7ff8 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -23,7 +23,6 @@
import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
-import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
@@ -60,12 +59,6 @@
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
- @Nullable
- @Override
- public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
- return client.getActivity(getActivityToken());
- }
-
// ObjectPoolItem implementation
private ActivityConfigurationChangeItem() {}
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index 6da871a..45bf235 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -23,7 +23,6 @@
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ResultInfo;
-import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.os.IBinder;
import android.os.Parcel;
@@ -88,12 +87,6 @@
client.reportRelaunch(r);
}
- @Nullable
- @Override
- public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
- return client.getActivity(getActivityToken());
- }
-
// ObjectPoolItem implementation
private ActivityRelaunchItem() {}
diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java
index a8d61db..99ebe1b 100644
--- a/core/java/android/app/servertransaction/ClientTransactionItem.java
+++ b/core/java/android/app/servertransaction/ClientTransactionItem.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ClientTransactionHandler;
-import android.content.Context;
import android.os.IBinder;
import android.os.Parcelable;
@@ -54,15 +53,6 @@
}
/**
- * If this {@link ClientTransactionItem} is updating configuration, returns the {@link Context}
- * it is updating; otherwise, returns {@code null}.
- */
- @Nullable
- public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
- return null;
- }
-
- /**
* Returns the activity token if this transaction item is activity-targeting. Otherwise,
* returns {@code null}.
*/
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index c55b0f1..c9b4aa1 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -16,16 +16,23 @@
package android.app.servertransaction;
+import static android.app.WindowConfiguration.areConfigurationsEqualForDisplay;
+
import static com.android.window.flags.Flags.activityWindowInfoFlag;
import static com.android.window.flags.Flags.bundleClientTransactionFlag;
import static java.util.Objects.requireNonNull;
+import android.annotation.AnyThread;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityThread;
+import android.content.Context;
+import android.content.res.Configuration;
import android.hardware.display.DisplayManagerGlobal;
import android.os.IBinder;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.window.ActivityWindowInfo;
@@ -51,6 +58,15 @@
private final ArraySet<BiConsumer<IBinder, ActivityWindowInfo>>
mActivityWindowInfoChangedListeners = new ArraySet<>();
+ /**
+ * Keeps track of the Context whose Configuration will get updated, mapping to the config before
+ * the change.
+ */
+ private final ArrayMap<Context, Configuration> mContextToPreChangedConfigMap = new ArrayMap<>();
+
+ /** Whether there is an {@link ClientTransaction} being executed. */
+ private boolean mIsClientTransactionExecuting;
+
/** Gets the singleton controller. */
@NonNull
public static ClientTransactionListenerController getInstance() {
@@ -80,6 +96,7 @@
* The listener will be invoked with two parameters: {@link Activity#getActivityToken()} and
* {@link ActivityWindowInfo}.
*/
+ @AnyThread
public void registerActivityWindowInfoChangedListener(
@NonNull BiConsumer<IBinder, ActivityWindowInfo> listener) {
if (!activityWindowInfoFlag()) {
@@ -94,6 +111,7 @@
* Unregisters the listener that was previously registered via
* {@link #registerActivityWindowInfoChangedListener(BiConsumer)}
*/
+ @AnyThread
public void unregisterActivityWindowInfoChangedListener(
@NonNull BiConsumer<IBinder, ActivityWindowInfo> listener) {
if (!activityWindowInfoFlag()) {
@@ -108,6 +126,7 @@
* Called when receives a {@link ClientTransaction} that is updating an activity's
* {@link ActivityWindowInfo}.
*/
+ @MainThread
public void onActivityWindowInfoChanged(@NonNull IBinder activityToken,
@NonNull ActivityWindowInfo activityWindowInfo) {
if (!activityWindowInfoFlag()) {
@@ -126,18 +145,96 @@
}
}
- /**
- * Called when receives a {@link ClientTransaction} that is updating display-related
- * window configuration.
- */
- public void onDisplayChanged(int displayId) {
- if (!bundleClientTransactionFlag()) {
- return;
- }
- if (ActivityThread.isSystem()) {
+ /** Called when starts executing a remote {@link ClientTransaction}. */
+ @MainThread
+ public void onClientTransactionStarted() {
+ mIsClientTransactionExecuting = true;
+ }
+
+ /** Called when finishes executing a remote {@link ClientTransaction}. */
+ @MainThread
+ public void onClientTransactionFinished() {
+ notifyDisplayManagerIfNeeded();
+ mIsClientTransactionExecuting = false;
+ }
+
+ /** Called before updating the Configuration of the given {@code context}. */
+ @MainThread
+ public void onContextConfigurationPreChanged(@NonNull Context context) {
+ if (!bundleClientTransactionFlag() || ActivityThread.isSystem()) {
// Not enable for system server.
return;
}
+ if (mContextToPreChangedConfigMap.containsKey(context)) {
+ // There is an earlier change that hasn't been reported yet.
+ return;
+ }
+ mContextToPreChangedConfigMap.put(context,
+ new Configuration(context.getResources().getConfiguration()));
+ }
+
+ /** Called after updating the Configuration of the given {@code context}. */
+ @MainThread
+ public void onContextConfigurationPostChanged(@NonNull Context context) {
+ if (!bundleClientTransactionFlag() || ActivityThread.isSystem()) {
+ // Not enable for system server.
+ return;
+ }
+ if (mIsClientTransactionExecuting) {
+ // Wait until #onClientTransactionFinished to prevent it from triggering the same
+ // #onDisplayChanged multiple times within the same ClientTransaction.
+ return;
+ }
+ final Configuration preChangedConfig = mContextToPreChangedConfigMap.remove(context);
+ if (preChangedConfig != null && shouldReportDisplayChange(context, preChangedConfig)) {
+ onDisplayChanged(context.getDisplayId());
+ }
+ }
+
+ /**
+ * When {@link Configuration} is changed, we want to trigger display change callback as well,
+ * because Display reads some fields from {@link Configuration}.
+ */
+ private void notifyDisplayManagerIfNeeded() {
+ if (mContextToPreChangedConfigMap.isEmpty()) {
+ return;
+ }
+ // Whether the configuration change should trigger DisplayListener#onDisplayChanged.
+ try {
+ // Calculate display ids that have config changed.
+ final ArraySet<Integer> configUpdatedDisplayIds = new ArraySet<>();
+ final int contextCount = mContextToPreChangedConfigMap.size();
+ for (int i = 0; i < contextCount; i++) {
+ final Context context = mContextToPreChangedConfigMap.keyAt(i);
+ final Configuration preChangedConfig = mContextToPreChangedConfigMap.valueAt(i);
+ if (shouldReportDisplayChange(context, preChangedConfig)) {
+ configUpdatedDisplayIds.add(context.getDisplayId());
+ }
+ }
+
+ // Dispatch the display changed callbacks.
+ final int displayCount = configUpdatedDisplayIds.size();
+ for (int i = 0; i < displayCount; i++) {
+ final int displayId = configUpdatedDisplayIds.valueAt(i);
+ onDisplayChanged(displayId);
+ }
+ } finally {
+ mContextToPreChangedConfigMap.clear();
+ }
+ }
+
+ private boolean shouldReportDisplayChange(@NonNull Context context,
+ @NonNull Configuration preChangedConfig) {
+ final Configuration postChangedConfig = context.getResources().getConfiguration();
+ return !areConfigurationsEqualForDisplay(postChangedConfig, preChangedConfig);
+ }
+
+ /**
+ * Called when receives a {@link Configuration} changed event that is updating display-related
+ * window configuration.
+ */
+ @VisibleForTesting
+ public void onDisplayChanged(int displayId) {
mDisplayManager.handleDisplayChangeFromWindowManager(displayId);
}
}
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index 0e327a7..22da706 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -18,9 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityThread;
import android.app.ClientTransactionHandler;
-import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.Parcel;
@@ -48,12 +46,6 @@
client.handleConfigurationChanged(mConfiguration, mDeviceId);
}
- @Nullable
- @Override
- public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
- return ActivityThread.currentApplication();
- }
-
// ObjectPoolItem implementation
private ConfigurationChangeItem() {}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index f02cb21..7dcbeba 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -24,14 +24,12 @@
import android.annotation.Nullable;
import android.app.ActivityClient;
import android.app.ActivityOptions.SceneTransitionInfo;
-import android.app.ActivityThread;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.IActivityClientController;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
@@ -121,13 +119,6 @@
client.countLaunchingActivities(-1);
}
- @Nullable
- @Override
- public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
- // LaunchActivityItem may update the global config with #mCurConfig.
- return ActivityThread.currentApplication();
- }
-
// ObjectPoolItem implementation
private LaunchActivityItem() {}
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 0702c45..8706edd 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -22,7 +22,6 @@
import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
-import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
@@ -59,12 +58,6 @@
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
- @Nullable
- @Override
- public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
- return client.getActivity(getActivityToken());
- }
-
// ObjectPoolItem implementation
private MoveToDisplayItem() {}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index c837191..480205e 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -16,7 +16,6 @@
package android.app.servertransaction;
-import static android.app.WindowConfiguration.areConfigurationsEqualForDisplay;
import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
@@ -32,17 +31,12 @@
import static android.app.servertransaction.TransactionExecutorHelper.tId;
import static android.app.servertransaction.TransactionExecutorHelper.transactionToString;
-import static com.android.window.flags.Flags.bundleClientTransactionFlag;
-
import android.annotation.NonNull;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.content.Context;
-import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Trace;
-import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
@@ -63,12 +57,6 @@
private final PendingTransactionActions mPendingActions = new PendingTransactionActions();
private final TransactionExecutorHelper mHelper = new TransactionExecutorHelper();
- /**
- * Keeps track of the Context whose Configuration got updated within a transaction, mapping to
- * the config before the transaction.
- */
- private final ArrayMap<Context, Configuration> mContextToPreChangedConfigMap = new ArrayMap<>();
-
/** Initialize an instance with transaction handler, that will execute all requested actions. */
public TransactionExecutor(@NonNull ClientTransactionHandler clientTransactionHandler) {
mTransactionHandler = clientTransactionHandler;
@@ -104,37 +92,6 @@
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
- if (!mContextToPreChangedConfigMap.isEmpty()) {
- // Whether this transaction should trigger DisplayListener#onDisplayChanged.
- try {
- // Calculate display ids that have config changed.
- final ArraySet<Integer> configUpdatedDisplayIds = new ArraySet<>();
- final int contextCount = mContextToPreChangedConfigMap.size();
- for (int i = 0; i < contextCount; i++) {
- final Context context = mContextToPreChangedConfigMap.keyAt(i);
- final Configuration preTransactionConfig =
- mContextToPreChangedConfigMap.valueAt(i);
- final Configuration postTransactionConfig = context.getResources()
- .getConfiguration();
- if (!areConfigurationsEqualForDisplay(
- postTransactionConfig, preTransactionConfig)) {
- configUpdatedDisplayIds.add(context.getDisplayId());
- }
- }
-
- // Dispatch the display changed callbacks.
- final ClientTransactionListenerController controller =
- ClientTransactionListenerController.getInstance();
- final int displayCount = configUpdatedDisplayIds.size();
- for (int i = 0; i < displayCount; i++) {
- final int displayId = configUpdatedDisplayIds.valueAt(i);
- controller.onDisplayChanged(displayId);
- }
- } finally {
- mContextToPreChangedConfigMap.clear();
- }
- }
-
mPendingActions.clear();
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}
@@ -214,20 +171,6 @@
}
}
- final boolean shouldTrackConfigUpdatedContext =
- // No configuration change for local transaction.
- !mTransactionHandler.isExecutingLocalTransaction()
- && bundleClientTransactionFlag();
- final Context configUpdatedContext = shouldTrackConfigUpdatedContext
- ? item.getContextToUpdate(mTransactionHandler)
- : null;
- if (configUpdatedContext != null
- && !mContextToPreChangedConfigMap.containsKey(configUpdatedContext)) {
- // Keep track of the first pre-executed config of each changed Context.
- mContextToPreChangedConfigMap.put(configUpdatedContext,
- new Configuration(configUpdatedContext.getResources().getConfiguration()));
- }
-
item.execute(mTransactionHandler, mPendingActions);
item.postExecute(mTransactionHandler, mPendingActions);
diff --git a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
index cbad92f..f6a7291 100644
--- a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
+++ b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
@@ -21,7 +21,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ClientTransactionHandler;
-import android.content.Context;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Parcel;
@@ -46,12 +45,6 @@
client.handleWindowContextInfoChanged(mClientToken, mInfo);
}
- @Nullable
- @Override
- public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
- return client.getWindowContext(mClientToken);
- }
-
// ObjectPoolItem implementation
private WindowContextInfoChangeItem() {}
diff --git a/core/java/android/app/servertransaction/WindowStateResizeItem.java b/core/java/android/app/servertransaction/WindowStateResizeItem.java
index 1817c5e..da99096 100644
--- a/core/java/android/app/servertransaction/WindowStateResizeItem.java
+++ b/core/java/android/app/servertransaction/WindowStateResizeItem.java
@@ -22,10 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityThread;
import android.app.ClientTransactionHandler;
-import android.content.Context;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.Trace;
@@ -59,10 +56,6 @@
/** {@code null} if this is not an Activity window. */
@Nullable
- private IBinder mActivityToken;
-
- /** {@code null} if this is not an Activity window. */
- @Nullable
private ActivityWindowInfo mActivityWindowInfo;
@Override
@@ -86,14 +79,6 @@
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
- @Nullable
- @Override
- public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
- // TODO(b/260873529): dispatch for mActivityToken as well.
- // WindowStateResizeItem may update the global config with #mConfiguration.
- return ActivityThread.currentApplication();
- }
-
// ObjectPoolItem implementation
private WindowStateResizeItem() {}
@@ -103,8 +88,7 @@
@NonNull ClientWindowFrames frames, boolean reportDraw,
@NonNull MergedConfiguration configuration, @NonNull InsetsState insetsState,
boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
- boolean dragResizing, @Nullable IBinder activityToken,
- @Nullable ActivityWindowInfo activityWindowInfo) {
+ boolean dragResizing, @Nullable ActivityWindowInfo activityWindowInfo) {
WindowStateResizeItem instance =
ObjectPool.obtain(WindowStateResizeItem.class);
if (instance == null) {
@@ -120,7 +104,6 @@
instance.mDisplayId = displayId;
instance.mSyncSeqId = syncSeqId;
instance.mDragResizing = dragResizing;
- instance.mActivityToken = activityToken;
instance.mActivityWindowInfo = activityWindowInfo != null
? new ActivityWindowInfo(activityWindowInfo)
: null;
@@ -140,7 +123,6 @@
mDisplayId = INVALID_DISPLAY;
mSyncSeqId = -1;
mDragResizing = false;
- mActivityToken = null;
mActivityWindowInfo = null;
ObjectPool.recycle(this);
}
@@ -160,7 +142,6 @@
dest.writeInt(mDisplayId);
dest.writeInt(mSyncSeqId);
dest.writeBoolean(mDragResizing);
- dest.writeStrongBinder(mActivityToken);
dest.writeTypedObject(mActivityWindowInfo, flags);
}
@@ -176,7 +157,6 @@
mDisplayId = in.readInt();
mSyncSeqId = in.readInt();
mDragResizing = in.readBoolean();
- mActivityToken = in.readStrongBinder();
mActivityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR);
}
@@ -209,7 +189,6 @@
&& mDisplayId == other.mDisplayId
&& mSyncSeqId == other.mSyncSeqId
&& mDragResizing == other.mDragResizing
- && Objects.equals(mActivityToken, other.mActivityToken)
&& Objects.equals(mActivityWindowInfo, other.mActivityWindowInfo);
}
@@ -226,7 +205,6 @@
result = 31 * result + mDisplayId;
result = 31 * result + mSyncSeqId;
result = 31 * result + (mDragResizing ? 1 : 0);
- result = 31 * result + Objects.hashCode(mActivityToken);
result = 31 * result + Objects.hashCode(mActivityWindowInfo);
return result;
}
@@ -236,7 +214,6 @@
return "WindowStateResizeItem{window=" + mWindow
+ ", reportDrawn=" + mReportDraw
+ ", configuration=" + mConfiguration
- + ", activityToken=" + mActivityToken
+ ", activityWindowInfo=" + mActivityWindowInfo
+ "}";
}
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index 4511954..765c802 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -32,3 +32,10 @@
description: "Enable support for transporting draw instructions as data parcel"
bug: "286130467"
}
+
+flag {
+ name: "throttle_widget_updates"
+ namespace: "app_widgets"
+ description: "Throttle the widget view updates to mitigate transaction exceptions"
+ bug: "326145514"
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b706cae..bad73fc 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3256,6 +3256,14 @@
*
* <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
*
+ * <p>As of {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the system can <a
+ * href="{@docRoot}develop/background-work/background-tasks/broadcasts#android-14">place
+ * context-registered broadcasts in a queue while the app is in the <a
+ * href="{@docRoot}guide/components/activities/process-lifecycle">cached state</a>.
+ * When the app leaves the cached state, such as returning to the
+ * foreground, the system delivers any queued broadcasts. Multiple instances
+ * of certain broadcasts might be merged into one broadcast.
+ *
* <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
* registered with this method will correctly respect the
* {@link Intent#setPackage(String)} specified for an Intent being broadcast.
@@ -3301,6 +3309,14 @@
*
* <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
*
+ * <p>As of {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the system can <a
+ * href="{@docRoot}develop/background-work/background-tasks/broadcasts#android-14">place
+ * context-registered broadcasts in a queue while the app is in the <a
+ * href="{@docRoot}guide/components/activities/process-lifecycle">cached state</a>.
+ * When the app leaves the cached state, such as returning to the
+ * foreground, the system delivers any queued broadcasts. Multiple instances
+ * of certain broadcasts might be merged into one broadcast.
+ *
* <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
* registered with this method will correctly respect the
* {@link Intent#setPackage(String)} specified for an Intent being broadcast.
@@ -3342,6 +3358,14 @@
*
* <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
*
+ * <p>As of {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the system can <a
+ * href="{@docRoot}develop/background-work/background-tasks/broadcasts#android-14">place
+ * context-registered broadcasts in a queue while the app is in the <a
+ * href="{@docRoot}guide/components/activities/process-lifecycle">cached state</a>.
+ * When the app leaves the cached state, such as returning to the
+ * foreground, the system delivers any queued broadcasts. Multiple instances
+ * of certain broadcasts might be merged into one broadcast.
+ *
* <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
* registered with this method will correctly respect the
* {@link Intent#setPackage(String)} specified for an Intent being broadcast.
@@ -3385,6 +3409,14 @@
*
* <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
*
+ * <p>As of {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the system can <a
+ * href="{@docRoot}develop/background-work/background-tasks/broadcasts#android-14">place
+ * context-registered broadcasts in a queue while the app is in the <a
+ * href="{@docRoot}guide/components/activities/process-lifecycle">cached state</a>.
+ * When the app leaves the cached state, such as returning to the
+ * foreground, the system delivers any queued broadcasts. Multiple instances
+ * of certain broadcasts might be merged into one broadcast.
+ *
* <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
* registered with this method will correctly respect the
* {@link Intent#setPackage(String)} specified for an Intent being broadcast.
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index 93fa5d8..481ff2e 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -61,7 +61,9 @@
@SystemService(Context.CREDENTIAL_SERVICE)
@RequiresFeature(PackageManager.FEATURE_CREDENTIALS)
public final class CredentialManager {
- private static final String TAG = "CredentialManager";
+ /** @hide **/
+ @Hide
+ public static final String TAG = "CredentialManager";
private static final Bundle OPTIONS_SENDER_BAL_OPTIN = ActivityOptions.makeBasic()
.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle();
@@ -132,6 +134,13 @@
"enable_credential_description_api";
/**
+ * @hide
+ */
+ @Hide
+ public static final String EXTRA_AUTOFILL_RESULT_RECEIVER =
+ "android.credentials.AUTOFILL_RESULT_RECEIVER";
+
+ /**
* @hide instantiated by ContextImpl.
*/
public CredentialManager(Context context, ICredentialManager service) {
diff --git a/core/java/android/credentials/selection/Constants.java b/core/java/android/credentials/selection/Constants.java
index 2229f25..a620621 100644
--- a/core/java/android/credentials/selection/Constants.java
+++ b/core/java/android/credentials/selection/Constants.java
@@ -28,12 +28,5 @@
*/
public static final String EXTRA_RESULT_RECEIVER =
"android.credentials.selection.extra.RESULT_RECEIVER";
-
- /**
- * The intent extra key for the final result receiver object
- */
- public static final String EXTRA_FINAL_RESPONSE_RECEIVER =
- "android.credentials.selection.extra.FINAL_RESPONSE_RECEIVER";
-
private Constants() {}
}
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index a51a740..d9a18d7 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -42,12 +42,6 @@
private static DdmHandleHello mInstance = new DdmHandleHello();
- private static final String[] FRAMEWORK_FEATURES = new String[] {
- "opengl-tracing",
- "view-hierarchy",
- "support_boot_stages"
- };
-
/* singleton, do not instantiate */
private DdmHandleHello() {}
@@ -193,22 +187,25 @@
if (false)
Log.v("ddm-heap", "Got feature list request");
- int size = 4 + 4 * (vmFeatures.length + FRAMEWORK_FEATURES.length);
- for (int i = vmFeatures.length-1; i >= 0; i--)
+ String[] fmFeatures = Debug.getFeatureList();
+ int size = 4 + 4 * (vmFeatures.length + fmFeatures.length);
+ for (int i = vmFeatures.length - 1; i >= 0; i--) {
size += vmFeatures[i].length() * 2;
- for (int i = FRAMEWORK_FEATURES.length-1; i>= 0; i--)
- size += FRAMEWORK_FEATURES[i].length() * 2;
+ }
+ for (int i = fmFeatures.length - 1; i >= 0; i--) {
+ size += fmFeatures[i].length() * 2;
+ }
ByteBuffer out = ByteBuffer.allocate(size);
out.order(ChunkHandler.CHUNK_ORDER);
- out.putInt(vmFeatures.length + FRAMEWORK_FEATURES.length);
+ out.putInt(vmFeatures.length + fmFeatures.length);
for (int i = vmFeatures.length-1; i >= 0; i--) {
out.putInt(vmFeatures[i].length());
putString(out, vmFeatures[i]);
}
- for (int i = FRAMEWORK_FEATURES.length-1; i >= 0; i--) {
- out.putInt(FRAMEWORK_FEATURES[i].length());
- putString(out, FRAMEWORK_FEATURES[i]);
+ for (int i = fmFeatures.length - 1; i >= 0; i--) {
+ out.putInt(fmFeatures[i].length());
+ putString(out, fmFeatures[i]);
}
return new Chunk(CHUNK_FEAT, out);
diff --git a/core/java/android/hardware/CameraSessionStats.java b/core/java/android/hardware/CameraSessionStats.java
index 32938ff..e5c12e3 100644
--- a/core/java/android/hardware/CameraSessionStats.java
+++ b/core/java/android/hardware/CameraSessionStats.java
@@ -16,9 +16,11 @@
package android.hardware;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Range;
import java.util.ArrayList;
import java.util.List;
+
/**
* The camera action state used for passing camera usage information from
* camera service to camera service proxy .
@@ -66,6 +68,7 @@
private int mVideoStabilizationMode;
private boolean mUsedUltraWide;
private boolean mUsedZoomOverride;
+ private Range<Integer> mMostRequestedFpsRange;
private int mSessionIndex;
private CameraExtensionSessionStats mCameraExtensionSessionStats;
@@ -86,6 +89,7 @@
mVideoStabilizationMode = -1;
mUsedUltraWide = false;
mUsedZoomOverride = false;
+ mMostRequestedFpsRange = new Range<Integer>(0, 0);
mSessionIndex = 0;
mCameraExtensionSessionStats = new CameraExtensionSessionStats();
}
@@ -109,6 +113,7 @@
mVideoStabilizationMode = -1;
mUsedUltraWide = false;
mUsedZoomOverride = false;
+ mMostRequestedFpsRange = new Range<Integer>(0, 0);
mSessionIndex = sessionIdx;
mCameraExtensionSessionStats = new CameraExtensionSessionStats();
}
@@ -158,6 +163,8 @@
dest.writeBoolean(mUsedZoomOverride);
dest.writeInt(mSessionIndex);
mCameraExtensionSessionStats.writeToParcel(dest, 0);
+ dest.writeInt(mMostRequestedFpsRange.getLower());
+ dest.writeInt(mMostRequestedFpsRange.getUpper());
}
public void readFromParcel(Parcel in) {
@@ -188,6 +195,9 @@
mSessionIndex = in.readInt();
mCameraExtensionSessionStats = CameraExtensionSessionStats.CREATOR.createFromParcel(in);
+ int minFps = in.readInt();
+ int maxFps = in.readInt();
+ mMostRequestedFpsRange = new Range<Integer>(minFps, maxFps);
}
public String getCameraId() {
@@ -273,4 +283,8 @@
public CameraExtensionSessionStats getExtensionSessionStats() {
return mCameraExtensionSessionStats;
}
+
+ public Range<Integer> getMostRequestedFpsRange() {
+ return mMostRequestedFpsRange;
+ }
}
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index dca663d..50d976f 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -1768,9 +1768,12 @@
* @param sessionConfig The session configuration for which characteristics are fetched.
* @return CameraCharacteristics specific to a given session configuration.
*
- * @throws IllegalArgumentException if the session configuration is invalid
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
+ * @throws IllegalArgumentException if the session configuration is invalid or if
+ * {@link #isSessionConfigurationSupported} returns
+ * {@code false} for the provided
+ * {@link SessionConfiguration}
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
*
* @see CameraCharacteristics#getAvailableSessionCharacteristicsKeys
*/
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 4cd40ea..90a2cf0 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1969,9 +1969,6 @@
private static final String TAG = "CameraManagerGlobal";
- private static final String BACK_CAMERA_ID = "0";
- private static final String FRONT_CAMERA_ID = "1";
-
private final boolean DEBUG = false;
private final int CAMERA_SERVICE_RECONNECT_DELAY_MS = 1000;
@@ -2309,11 +2306,6 @@
return false;
}
- // External cameras should never be hidden.
- if (!info.mCameraId.equals(FRONT_CAMERA_ID) && !info.mCameraId.equals(BACK_CAMERA_ID)) {
- return false;
- }
-
return currentDeviceId != info.mDeviceId;
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
index 81d0976..372839d 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
@@ -104,8 +104,8 @@
}
try {
- return cameraService.isSessionConfigurationWithParametersSupported(
- mCameraId, config, mContext.getDeviceId(),
+ return cameraService.isSessionConfigurationWithParametersSupported(mCameraId,
+ mTargetSdkVersion, config, mContext.getDeviceId(),
mCameraManager.getDevicePolicyFromContext(mContext));
} catch (ServiceSpecificException e) {
throw ExceptionUtils.throwAsPublicException(e);
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 553d9f7..5a0f3db 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -164,15 +164,9 @@
void getFeature(IBinder token, int userId, int feature, IFaceServiceReceiver receiver,
String opPackageName);
- // Registers all HIDL and AIDL sensors. Only HIDL sensor properties need to be provided, because
- // AIDL sensor properties are retrieved directly from the available HALs. If no HIDL HALs exist,
- // hidlSensors must be non-null and empty. See AuthService.java
- @EnforcePermission("USE_BIOMETRIC_INTERNAL")
- void registerAuthenticators(in List<FaceSensorPropertiesInternal> hidlSensors);
-
//Register all available face sensors.
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
- void registerAuthenticatorsLegacy(in FaceSensorConfigurations faceSensorConfigurations);
+ void registerAuthenticators(in FaceSensorConfigurations faceSensorConfigurations);
// Adds a callback which gets called when the service registers all of the face
// authenticators. The callback is automatically removed after it's invoked.
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 8b37c24..6a96ac4 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -177,13 +177,7 @@
//Register all available fingerprint sensors.
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
- void registerAuthenticatorsLegacy(in FingerprintSensorConfigurations fingerprintSensorConfigurations);
-
- // Registers all HIDL and AIDL sensors. Only HIDL sensor properties need to be provided, because
- // AIDL sensor properties are retrieved directly from the available HALs. If no HIDL HALs exist,
- // hidlSensors must be non-null and empty. See AuthService.java
- @EnforcePermission("USE_BIOMETRIC_INTERNAL")
- void registerAuthenticators(in List<FingerprintSensorPropertiesInternal> hidlSensors);
+ void registerAuthenticators(in FingerprintSensorConfigurations fingerprintSensorConfigurations);
// Adds a callback which gets called when the service registers all of the fingerprint
// authenticators. The callback is automatically removed after it's invoked.
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 1c37aa2..2d474d6 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -165,10 +165,17 @@
// static association for the cleared input port will be restored.
void removePortAssociation(in String inputPort);
- // Add a runtime association between the input device and display.
- void addUniqueIdAssociation(in String inputPort, in String displayUniqueId);
- // Remove the runtime association between the input device and display.
- void removeUniqueIdAssociation(in String inputPort);
+ // Add a runtime association between the input device and display, using device's descriptor.
+ void addUniqueIdAssociationByDescriptor(in String inputDeviceDescriptor,
+ in String displayUniqueId);
+ // Remove the runtime association between the input device and display, using device's
+ // descriptor.
+ void removeUniqueIdAssociationByDescriptor(in String inputDeviceDescriptor);
+
+ // Add a runtime association between the input device and display, using device's port.
+ void addUniqueIdAssociationByPort(in String inputPort, in String displayUniqueId);
+ // Remove the runtime association between the input device and display, using device's port.
+ void removeUniqueIdAssociationByPort(in String inputPort);
InputSensorInfo[] getSensorList(int deviceId);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index f949158..4dda2c7 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -17,6 +17,7 @@
package android.hardware.input;
import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;
+import static com.android.input.flags.Flags.FLAG_DEVICE_ASSOCIATIONS;
import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag;
import android.Manifest;
@@ -1054,13 +1055,14 @@
/**
* Add a runtime association between the input port and the display port. This overrides any
* static associations.
- * @param inputPort The port of the input device.
- * @param displayPort The physical port of the associated display.
+ * @param inputPort the port of the input device
+ * @param displayPort the physical port of the associated display
* <p>
* Requires {@link android.Manifest.permission#ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY)
public void addPortAssociation(@NonNull String inputPort, int displayPort) {
try {
mIm.addPortAssociation(inputPort, displayPort);
@@ -1072,12 +1074,13 @@
/**
* Remove the runtime association between the input port and the display port. Any existing
* static association for the cleared input port will be restored.
- * @param inputPort The port of the input device to be cleared.
+ * @param inputPort the port of the input device to be cleared
* <p>
* Requires {@link android.Manifest.permission#ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY)
public void removePortAssociation(@NonNull String inputPort) {
try {
mIm.removePortAssociation(inputPort);
@@ -1089,30 +1092,74 @@
/**
* Add a runtime association between the input port and display, by unique id. Input ports are
* expected to be unique.
- * @param inputPort The port of the input device.
- * @param displayUniqueId The unique id of the associated display.
+ * @param inputPort the port of the input device
+ * @param displayUniqueId the unique id of the associated display
* <p>
* Requires {@link android.Manifest.permission#ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
+ @FlaggedApi(FLAG_DEVICE_ASSOCIATIONS)
+ @RequiresPermission(android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY)
@TestApi
- public void addUniqueIdAssociation(@NonNull String inputPort,
+ public void addUniqueIdAssociationByPort(@NonNull String inputPort,
@NonNull String displayUniqueId) {
- mGlobal.addUniqueIdAssociation(inputPort, displayUniqueId);
+ mGlobal.addUniqueIdAssociationByPort(inputPort, displayUniqueId);
}
/**
* Removes a runtime association between the input device and display.
- * @param inputPort The port of the input device.
+ * @param inputPort the port of the input device
* <p>
* Requires {@link android.Manifest.permission#ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
+ @FlaggedApi(FLAG_DEVICE_ASSOCIATIONS)
+ @RequiresPermission(android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY)
@TestApi
- public void removeUniqueIdAssociation(@NonNull String inputPort) {
- mGlobal.removeUniqueIdAssociation(inputPort);
+ public void removeUniqueIdAssociationByPort(@NonNull String inputPort) {
+ mGlobal.removeUniqueIdAssociationByPort(inputPort);
+ }
+
+ /**
+ * Add a runtime association between the input device name and display, by descriptor. Input
+ * device descriptors are expected to be unique per physical device, though one physical
+ * device can have multiple virtual input devices that possess the same descriptor.
+ * E.g. a keyboard with built in trackpad will be 2 different input devices with the same
+ * descriptor.
+ * @param inputDeviceDescriptor the descriptor of the input device
+ * @param displayUniqueId the unique id of the associated display
+ * <p>
+ * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * </p>
+ * @hide
+ */
+ @FlaggedApi(FLAG_DEVICE_ASSOCIATIONS)
+ @RequiresPermission(android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY)
+ @TestApi
+ public void addUniqueIdAssociationByDescriptor(@NonNull String inputDeviceDescriptor,
+ @NonNull String displayUniqueId) {
+ mGlobal.addUniqueIdAssociationByDescriptor(inputDeviceDescriptor, displayUniqueId);
+ }
+
+ /**
+ * Removes a runtime association between the input device and display.
+ }
+
+ /**
+ * Removes a runtime association between the input device and display.
+ * @param inputDeviceDescriptor the descriptor of the input device
+ * <p>
+ * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * </p>
+ * @hide
+ */
+ @FlaggedApi(FLAG_DEVICE_ASSOCIATIONS)
+ @RequiresPermission(android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY)
+ @TestApi
+ public void removeUniqueIdAssociationByDescriptor(@NonNull String inputDeviceDescriptor) {
+ mGlobal.removeUniqueIdAssociationByDescriptor(inputDeviceDescriptor);
}
/**
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index 7b29666..1d253d9 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -1467,22 +1467,46 @@
}
/**
- * @see InputManager#addUniqueIdAssociation(String, String)
+ * @see InputManager#addUniqueIdAssociationByPort(String, String)
*/
- public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) {
+ public void addUniqueIdAssociationByPort(@NonNull String inputPort,
+ @NonNull String displayUniqueId) {
try {
- mIm.addUniqueIdAssociation(inputPort, displayUniqueId);
+ mIm.addUniqueIdAssociationByPort(inputPort, displayUniqueId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * @see InputManager#removeUniqueIdAssociation(String)
+ * @see InputManager#removeUniqueIdAssociationByPort(String)
*/
- public void removeUniqueIdAssociation(@NonNull String inputPort) {
+ public void removeUniqueIdAssociationByPort(@NonNull String inputPort) {
try {
- mIm.removeUniqueIdAssociation(inputPort);
+ mIm.removeUniqueIdAssociationByPort(inputPort);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @see InputManager#addUniqueIdAssociationByDescriptor(String, String)
+ */
+ public void addUniqueIdAssociationByDescriptor(@NonNull String inputDeviceDescriptor,
+ @NonNull String displayUniqueId) {
+ try {
+ mIm.addUniqueIdAssociationByDescriptor(inputDeviceDescriptor, displayUniqueId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @see InputManager#removeUniqueIdAssociationByDescriptor(String)
+ */
+ public void removeUniqueIdAssociationByDescriptor(@NonNull String inputDeviceDescriptor) {
+ try {
+ mIm.removeUniqueIdAssociationByDescriptor(inputDeviceDescriptor);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 6246dd7..91cdf8d 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -124,6 +124,22 @@
"vcn_network_selection_ipsec_packet_loss_percent_threshold";
/**
+ * Key for detecting unusually large increases in IPsec packet sequence numbers.
+ *
+ * <p>If the sequence number increases by more than this value within a second, it may indicate
+ * an intentional leap on the server's downlink. To avoid false positives, the packet loss
+ * detector will suppress loss reporting.
+ *
+ * <p>By default, there's no maximum limit enforced, prioritizing detection of lossy networks.
+ * To reduce false positives, consider setting an appropriate maximum threshold.
+ *
+ * @hide
+ */
+ @NonNull
+ public static final String VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY =
+ "vcn_network_selection_max_seq_num_increase_per_second";
+
+ /**
* Key for the list of timeouts in minute to stop penalizing an underlying network candidate
*
* @hide
@@ -180,6 +196,7 @@
VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY,
VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY,
+ VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY,
VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY,
VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY,
VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY,
diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
index e64823a..6fde398 100644
--- a/core/java/android/net/vcn/flags.aconfig
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -34,4 +34,14 @@
namespace: "vcn"
description: "Re-evaluate IPsec packet loss on LinkProperties or NetworkCapabilities change"
bug: "323238888"
+}
+
+flag{
+ name: "handle_seq_num_leap"
+ namespace: "vcn"
+ description: "Do not report bad network when there is a suspected sequence number leap"
+ bug: "332598276"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index f785cca..a55398a 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -110,6 +110,12 @@
private static final String DEFAULT_TRACE_BODY = "dmtrace";
private static final String DEFAULT_TRACE_EXTENSION = ".trace";
+ private static final String[] FRAMEWORK_FEATURES = new String[] {
+ "opengl-tracing",
+ "view-hierarchy",
+ "support_boot_stages",
+ };
+
/**
* This class is used to retrieved various statistics about the memory mappings for this
* process. The returned info is broken down by dalvik, native, and other. All results are in kB.
@@ -1106,6 +1112,17 @@
}
/**
+ * Returns an array of strings that identify Framework features. This is
+ * used by DDMS to determine what sorts of operations the Framework can
+ * perform.
+ *
+ * @hide
+ */
+ public static String[] getFeatureList() {
+ return FRAMEWORK_FEATURES;
+ }
+
+ /**
* Change the JDWP port.
*
* @deprecated no longer needed or useful
diff --git a/core/java/android/os/OomKillRecord.java b/core/java/android/os/OomKillRecord.java
index ca1d49a..78fbce6 100644
--- a/core/java/android/os/OomKillRecord.java
+++ b/core/java/android/os/OomKillRecord.java
@@ -25,7 +25,7 @@
* Note that this class fields' should be equivalent to the struct
* <b>OomKill</b> inside
* <pre>
- * system/memory/libmeminfo/libmemevents/include/memevents.h
+ * system/memory/libmeminfo/libmemevents/include/memevents/bpf_types.h
* </pre>
*
* @hide
@@ -36,14 +36,27 @@
private int mUid;
private String mProcessName;
private short mOomScoreAdj;
+ private long mTotalVmInKb;
+ private long mAnonRssInKb;
+ private long mFileRssInKb;
+ private long mShmemRssInKb;
+ private long mPgTablesInKb;
public OomKillRecord(long timeStampInMillis, int pid, int uid,
- String processName, short oomScoreAdj) {
+ String processName, short oomScoreAdj,
+ long totalVmInKb, long anonRssInKb,
+ long fileRssInKb, long shmemRssInKb,
+ long pgTablesInKb) {
this.mTimeStampInMillis = timeStampInMillis;
this.mPid = pid;
this.mUid = uid;
this.mProcessName = processName;
this.mOomScoreAdj = oomScoreAdj;
+ this.mTotalVmInKb = totalVmInKb;
+ this.mAnonRssInKb = anonRssInKb;
+ this.mFileRssInKb = fileRssInKb;
+ this.mShmemRssInKb = shmemRssInKb;
+ this.mPgTablesInKb = pgTablesInKb;
}
/**
@@ -55,7 +68,8 @@
FrameworkStatsLog.write(
FrameworkStatsLog.KERNEL_OOM_KILL_OCCURRED,
mUid, mPid, mOomScoreAdj, mTimeStampInMillis,
- mProcessName);
+ mProcessName, mTotalVmInKb, mAnonRssInKb,
+ mFileRssInKb, mShmemRssInKb, mPgTablesInKb);
}
public long getTimestampMilli() {
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 35a3a5f..136c45d 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -863,6 +863,28 @@
}
/** @hide */
+ public void removeClassCookie(Class clz, Object expectedCookie) {
+ if (mClassCookies != null) {
+ Object removedCookie = mClassCookies.remove(clz);
+ if (removedCookie != expectedCookie) {
+ Log.wtf(TAG, "Expected to remove " + expectedCookie + " (with key=" + clz
+ + ") but instead removed " + removedCookie);
+ }
+ } else {
+ Log.wtf(TAG, "Expected to remove " + expectedCookie + " (with key=" + clz
+ + ") but no cookies were present");
+ }
+ }
+
+ /**
+ * Whether {@link #setClassCookie} has been called with the specified {@code clz}.
+ * @hide
+ */
+ public boolean hasClassCookie(Class clz) {
+ return mClassCookies != null && mClassCookies.containsKey(clz);
+ }
+
+ /** @hide */
public final void adoptClassCookies(Parcel from) {
mClassCookies = from.mClassCookies;
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index f172c3e..857a85d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -705,7 +705,7 @@
* {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_BLUETOOTH}
* can set this restriction using the DevicePolicyManager APIs mentioned below.
*
- * <p>Default is <code>true</code> for managed profiles and false otherwise.
+ * <p>Default is <code>true</code> for managed and private profiles, false otherwise.
*
* <p>When a device upgrades to {@link android.os.Build.VERSION_CODES#O}, the system sets it
* for all existing managed profiles.
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 4ae0a57..3b59901 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -98,6 +98,8 @@
IBinder registerAttributionSource(in AttributionSourceState source);
+ int getNumRegisteredAttributionSources(int uid);
+
boolean isRegisteredAttributionSource(in AttributionSourceState source);
int checkPermission(String packageName, String permissionName, String persistentDeviceId,
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index fe3fa8c..2daf4ac 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1675,6 +1675,21 @@
}
/**
+ * Gets the number of currently registered attribution sources for a particular UID. This should
+ * only be used for testing purposes.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.UPDATE_APP_OPS_STATS)
+ public int getNumRegisteredAttributionSourcesForTest(int uid) {
+ try {
+ return mPermissionManager.getNumRegisteredAttributionSources(uid);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return -1;
+ }
+
+ /**
* Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
* USED in CTS or local tests.
*
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index ec3c978..abb4917 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -125,6 +125,30 @@
}
flag {
+ name: "sensitive_content_metrics_bugfix"
+ namespace: "permissions"
+ description: "Enables metrics bugfixes for sensitive content/notification features"
+ bug: "312784351"
+ # Referenced in WM where WM starts before DeviceConfig
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "sensitive_content_recents_screenshot_bugfix"
+ namespace: "permissions"
+ description: "Enables recents screenshot bugfixes for sensitive content/notification features"
+ bug: "312784351"
+ # Referenced in WM where WM starts before DeviceConfig
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "device_aware_permissions_enabled"
is_fixed_read_only: true
namespace: "permissions"
@@ -155,14 +179,3 @@
description: "Use runtime permission state to determine appop state"
bug: "266164193"
}
-
-flag {
- name: "ignore_apex_permissions"
- is_fixed_read_only: true
- namespace: "permissions"
- description: "Ignore APEX pacakges for permissions on V+"
- bug: "301320911"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
diff --git a/core/java/android/security/FileIntegrityManager.java b/core/java/android/security/FileIntegrityManager.java
index 025aac9..478435b 100644
--- a/core/java/android/security/FileIntegrityManager.java
+++ b/core/java/android/security/FileIntegrityManager.java
@@ -20,6 +20,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.IInstalld.IFsveritySetupAuthToken;
@@ -99,8 +101,11 @@
* @throws IOException If the operation failed.
*
* @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
+ * @hide
*/
@FlaggedApi(Flags.FLAG_FSVERITY_API)
+ @SuppressLint("StreamFiles")
+ @SystemApi
public void setupFsVerity(@NonNull File file) throws IOException {
if (!file.isAbsolute()) {
// fs-verity is to be enabled by installd, which enforces the validation to the
@@ -138,8 +143,11 @@
* @param file The file to measure the fs-verity digest.
* @return The fs-verity digest in byte[], null if none.
* @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
+ * @hide
*/
@FlaggedApi(Flags.FLAG_FSVERITY_API)
+ @SuppressLint("StreamFiles")
+ @SystemApi
public @Nullable byte[] getFsVerityDigest(@NonNull File file) throws IOException {
return VerityUtils.getFsverityDigest(file.getPath());
}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index e6a84df..269839b 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -37,7 +37,6 @@
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
-import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.os.IResultReceiver;
@@ -642,7 +641,7 @@
@Override
public void onFillCredentialRequest(FillRequest request, IFillCallback callback,
- IAutoFillManagerClient autofillClientCallback) {
+ IBinder autofillClientCallback) {
ICancellationSignal transport = CancellationSignal.createTransport();
try {
callback.onCancellable(transport);
@@ -724,7 +723,7 @@
*/
public void onFillCredentialRequest(@NonNull FillRequest request,
@NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback,
- @NonNull IAutoFillManagerClient autofillClientCallback) {}
+ @NonNull IBinder autofillClientCallback) {}
/**
* Called by the Android system to convert a credential manager response to a dataset
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index 2c2feae..3b64b8a 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -16,13 +16,13 @@
package android.service.autofill;
+import android.os.IBinder;
import android.service.autofill.ConvertCredentialRequest;
import android.service.autofill.IConvertCredentialCallback;
import android.service.autofill.FillRequest;
import android.service.autofill.IFillCallback;
import android.service.autofill.ISaveCallback;
import android.service.autofill.SaveRequest;
-import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.os.IResultReceiver;
/**
@@ -34,7 +34,7 @@
void onConnectedStateChanged(boolean connected);
void onFillRequest(in FillRequest request, in IFillCallback callback);
void onFillCredentialRequest(in FillRequest request, in IFillCallback callback,
- in IAutoFillManagerClient client);
+ in IBinder client);
void onSaveRequest(in SaveRequest request, in ISaveCallback callback);
void onSavedPasswordCountRequest(in IResultReceiver receiver);
void onConvertCredentialRequest(in ConvertCredentialRequest convertCredentialRequest, in IConvertCredentialCallback convertCredentialCallback);
diff --git a/core/java/android/service/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index c6d3d9b..92f2c32 100644
--- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -65,7 +65,7 @@
* @hide
*/
public final class CredentialProviderInfoFactory {
- private static final String TAG = "CredentialProviderInfoFactory";
+ private static final String TAG = CredentialManager.TAG;
private static final String TAG_CREDENTIAL_PROVIDER = "credential-provider";
private static final String TAG_CAPABILITIES = "capabilities";
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 9696dbc..9c14946 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -20,6 +20,7 @@
import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.app.Notification;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -146,8 +147,13 @@
/**
* Data type: boolean, when true it suggests that the content text of this notification is
- * sensitive. A notification listener can use this information to redact notifications on locked
- * devices.
+ * sensitive. The system uses this information to improve privacy around the notification
+ * content. In {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, sensitive notification content is
+ * redacted from updates to most {@link NotificationListenerService
+ * NotificationListenerServices}. Also if an app posts a sensitive notification while
+ * {@link android.media.projection.MediaProjection screen-sharing} is active, that app's windows
+ * are blocked from screen-sharing and a {@link android.widget.Toast Toast} is shown to inform
+ * the user about this.
*/
public static final String KEY_SENSITIVE_CONTENT = "key_sensitive_content";
diff --git a/core/java/android/tracing/perfetto/DataSource.java b/core/java/android/tracing/perfetto/DataSource.java
index c33fa7d..b9ab82c 100644
--- a/core/java/android/tracing/perfetto/DataSource.java
+++ b/core/java/android/tracing/perfetto/DataSource.java
@@ -18,10 +18,6 @@
import android.util.proto.ProtoInputStream;
-import com.android.internal.annotations.VisibleForTesting;
-
-import dalvik.annotation.optimization.CriticalNative;
-
/**
* Templated base class meant to be derived by embedders to create a custom data
* source.
@@ -73,7 +69,8 @@
* @param fun The tracing lambda that will be called with the tracing contexts of each active
* tracing instance.
*/
- public final void trace(TraceFunction<TlsStateType, IncrementalStateType> fun) {
+ public final void trace(
+ TraceFunction<DataSourceInstanceType, TlsStateType, IncrementalStateType> fun) {
boolean startedIterator = nativePerfettoDsTraceIterateBegin(mNativeObj);
if (!startedIterator) {
@@ -82,8 +79,10 @@
try {
do {
- TracingContext<TlsStateType, IncrementalStateType> ctx =
- new TracingContext<>(mNativeObj);
+ int instanceIndex = nativeGetPerfettoDsInstanceIndex(mNativeObj);
+
+ TracingContext<DataSourceInstanceType, TlsStateType, IncrementalStateType> ctx =
+ new TracingContext<>(this, instanceIndex);
fun.trace(ctx);
ctx.flush();
@@ -104,9 +103,7 @@
* Override this method to create a custom TlsState object for your DataSource. A new instance
* will be created per trace instance per thread.
*
- * NOTE: Should only be called from native side.
*/
- @VisibleForTesting
public TlsStateType createTlsState(CreateTlsStateArgs<DataSourceInstanceType> args) {
return null;
}
@@ -114,9 +111,8 @@
/**
* Override this method to create and use a custom IncrementalState object for your DataSource.
*
- * NOTE: Should only be called from native side.
*/
- protected IncrementalStateType createIncrementalState(
+ public IncrementalStateType createIncrementalState(
CreateIncrementalStateArgs<DataSourceInstanceType> args) {
return null;
}
@@ -179,10 +175,8 @@
private static native void nativeReleasePerfettoInstanceLocked(
long dataSourcePtr, int dsInstanceIdx);
- @CriticalNative
private static native boolean nativePerfettoDsTraceIterateBegin(long dataSourcePtr);
- @CriticalNative
private static native boolean nativePerfettoDsTraceIterateNext(long dataSourcePtr);
- @CriticalNative
private static native void nativePerfettoDsTraceIterateBreak(long dataSourcePtr);
+ private static native int nativeGetPerfettoDsInstanceIndex(long dataSourcePtr);
}
diff --git a/core/java/android/tracing/perfetto/TraceFunction.java b/core/java/android/tracing/perfetto/TraceFunction.java
index 13e663d..d8854f9 100644
--- a/core/java/android/tracing/perfetto/TraceFunction.java
+++ b/core/java/android/tracing/perfetto/TraceFunction.java
@@ -19,12 +19,14 @@
/**
* The interface for the trace function called from native on a trace call with a context.
*
+ * @param <DataSourceInstanceType> The type of DataSource this tracing context is for.
* @param <TlsStateType> The type of the custom TLS state, if any is used.
* @param <IncrementalStateType> The type of the custom incremental state, if any is used.
*
* @hide
*/
-public interface TraceFunction<TlsStateType, IncrementalStateType> {
+public interface TraceFunction<DataSourceInstanceType extends DataSourceInstance,
+ TlsStateType, IncrementalStateType> {
/**
* This function will be called synchronously (i.e., always before trace() returns) only if
@@ -34,5 +36,5 @@
*
* @param ctx the tracing context to trace for in the trace function.
*/
- void trace(TracingContext<TlsStateType, IncrementalStateType> ctx);
+ void trace(TracingContext<DataSourceInstanceType, TlsStateType, IncrementalStateType> ctx);
}
diff --git a/core/java/android/tracing/perfetto/TracingContext.java b/core/java/android/tracing/perfetto/TracingContext.java
index 060f964..6b7df54 100644
--- a/core/java/android/tracing/perfetto/TracingContext.java
+++ b/core/java/android/tracing/perfetto/TracingContext.java
@@ -24,18 +24,25 @@
/**
* Argument passed to the lambda function passed to Trace().
*
+ * @param <DataSourceInstanceType> The type of the datasource this tracing context is for.
* @param <TlsStateType> The type of the custom TLS state, if any is used.
* @param <IncrementalStateType> The type of the custom incremental state, if any is used.
*
* @hide
*/
-public class TracingContext<TlsStateType, IncrementalStateType> {
+public class TracingContext<DataSourceInstanceType extends DataSourceInstance, TlsStateType,
+ IncrementalStateType> {
- private final long mNativeDsPtr;
+ private final DataSource<DataSourceInstanceType, TlsStateType, IncrementalStateType>
+ mDataSource;
+ private final int mInstanceIndex;
private final List<ProtoOutputStream> mTracePackets = new ArrayList<>();
- TracingContext(long nativeDsPtr) {
- this.mNativeDsPtr = nativeDsPtr;
+ TracingContext(DataSource<DataSourceInstanceType, TlsStateType, IncrementalStateType>
+ dataSource,
+ int instanceIndex) {
+ this.mDataSource = dataSource;
+ this.mInstanceIndex = instanceIndex;
}
/**
@@ -61,18 +68,26 @@
* Stop timeout expires.
*/
public void flush() {
- nativeFlush(mNativeDsPtr, getAndClearAllPendingTracePackets());
+ nativeFlush(mDataSource.mNativeObj, getAndClearAllPendingTracePackets());
}
/**
* Can optionally be used to store custom per-sequence
* session data, which is not reset when incremental state is cleared
* (e.g. configuration options).
- *
+ *h
* @return The TlsState instance for the tracing thread and instance.
*/
public TlsStateType getCustomTlsState() {
- return (TlsStateType) nativeGetCustomTls(mNativeDsPtr);
+ TlsStateType tlsState = (TlsStateType) nativeGetCustomTls(mDataSource.mNativeObj);
+ if (tlsState == null) {
+ final CreateTlsStateArgs<DataSourceInstanceType> args =
+ new CreateTlsStateArgs<>(mDataSource, mInstanceIndex);
+ tlsState = mDataSource.createTlsState(args);
+ nativeSetCustomTls(mDataSource.mNativeObj, tlsState);
+ }
+
+ return tlsState;
}
/**
@@ -82,7 +97,16 @@
* @return The current IncrementalState object instance.
*/
public IncrementalStateType getIncrementalState() {
- return (IncrementalStateType) nativeGetIncrementalState(mNativeDsPtr);
+ IncrementalStateType incrementalState =
+ (IncrementalStateType) nativeGetIncrementalState(mDataSource.mNativeObj);
+ if (incrementalState == null) {
+ final CreateIncrementalStateArgs<DataSourceInstanceType> args =
+ new CreateIncrementalStateArgs<>(mDataSource, mInstanceIndex);
+ incrementalState = mDataSource.createIncrementalState(args);
+ nativeSetIncrementalState(mDataSource.mNativeObj, incrementalState);
+ }
+
+ return incrementalState;
}
private byte[][] getAndClearAllPendingTracePackets() {
@@ -97,6 +121,10 @@
}
private static native void nativeFlush(long dataSourcePtr, byte[][] packetData);
+
private static native Object nativeGetCustomTls(long nativeDsPtr);
+ private static native void nativeSetCustomTls(long nativeDsPtr, Object tlsState);
+
private static native Object nativeGetIncrementalState(long nativeDsPtr);
+ private static native void nativeSetIncrementalState(long nativeDsPtr, Object incrementalState);
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 4d4cb6c..815fc58 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -87,12 +87,6 @@
"settings_need_connected_ble_device_for_broadcast";
/**
- * Enable new language and keyboard settings UI
- * @hide
- */
- public static final String SETTINGS_NEW_KEYBOARD_UI = "settings_new_keyboard_ui";
-
- /**
* Enable new modifier key settings UI
* @hide
*/
@@ -221,7 +215,6 @@
DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true");
DEFAULT_FLAGS.put(SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, "true");
- DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_UI, "true");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_MODIFIER_KEY, "true");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD, "true");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE, "false");
@@ -249,7 +242,6 @@
PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
PERSISTENT_FLAGS.add(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME);
- PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_UI);
PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_MODIFIER_KEY);
PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_TRACKPAD);
PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE);
diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java
index ea7efc7..c1ed19f 100644
--- a/core/java/android/util/PackageUtils.java
+++ b/core/java/android/util/PackageUtils.java
@@ -203,9 +203,8 @@
}
File f = new File(filePath);
- try {
- DigestInputStream digestInputStream = new DigestInputStream(new FileInputStream(f),
- messageDigest);
+ try (DigestInputStream digestInputStream = new DigestInputStream(new FileInputStream(f),
+ messageDigest)) {
while (digestInputStream.read(fileBuffer) != -1);
} catch (IOException e) {
e.printStackTrace();
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 3e61539..f315f55 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -48,6 +48,7 @@
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityRequestPreparer;
+import android.view.accessibility.Flags;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.window.ScreenCapture;
@@ -1286,6 +1287,15 @@
}
/**
+ * Destroy {@link AccessibilityInteractionController} and clean up the pending actions.
+ */
+ public void destroy() {
+ if (Flags.preventLeakingViewrootimpl()) {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+ }
+
+ /**
* This class encapsulates a prefetching strategy for the accessibility APIs for
* querying window content. It is responsible to prefetch a batch of
* AccessibilityNodeInfos in addition to the one for a requested node.
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index f4dadbb..beb4d95 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -17,6 +17,7 @@
package android.view;
import static com.android.text.flags.Flags.handwritingCursorPosition;
+import static com.android.text.flags.Flags.handwritingUnsupportedMessage;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
@@ -607,7 +608,9 @@
final View candidateView = findBestCandidateView(hoverX, hoverY, /* isHover */ true);
if (candidateView != null) {
- mCachedHoverTarget = new WeakReference<>(candidateView);
+ if (!handwritingUnsupportedMessage()) {
+ mCachedHoverTarget = new WeakReference<>(candidateView);
+ }
return candidateView;
}
}
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 253073a..69228ca 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -83,11 +83,7 @@
*/
public static final int TEXT_HANDLE_MOVE = 9;
- /**
- * The user unlocked the device
- * @hide
- */
- public static final int ENTRY_BUMP = 10;
+ // REMOVED: ENTRY_BUMP = 10
/**
* The user has moved the dragged object within a droppable area.
@@ -230,6 +226,22 @@
public static final int LONG_PRESS_POWER_BUTTON = 10003;
/**
+ * A haptic effect to signal the confirmation of a user biometric authentication
+ * (e.g. fingerprint reading).
+ * This is a private constant to be used only by system apps.
+ * @hide
+ */
+ public static final int BIOMETRIC_CONFIRM = 10004;
+
+ /**
+ * A haptic effect to signal the rejection of a user biometric authentication attempt
+ * (e.g. fingerprint reading).
+ * This is a private constant to be used only by system apps.
+ * @hide
+ */
+ public static final int BIOMETRIC_REJECT = 10005;
+
+ /**
* Flag for {@link View#performHapticFeedback(int, int)
* View.performHapticFeedback(int, int)}: Ignore the setting in the
* view for whether to perform haptic feedback, do it always.
diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java
index d14e858..665fac1 100644
--- a/core/java/android/view/ImeBackAnimationController.java
+++ b/core/java/android/view/ImeBackAnimationController.java
@@ -28,6 +28,7 @@
import android.annotation.Nullable;
import android.graphics.Insets;
import android.util.Log;
+import android.view.animation.BackGestureInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.window.BackEvent;
@@ -44,7 +45,7 @@
private static final int POST_COMMIT_DURATION_MS = 200;
private static final int POST_COMMIT_CANCEL_DURATION_MS = 50;
private static final float PEEK_FRACTION = 0.1f;
- private static final Interpolator STANDARD_DECELERATE = new PathInterpolator(0f, 0f, 0f, 1f);
+ private static final Interpolator BACK_GESTURE = new BackGestureInterpolator();
private static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
0.05f, 0.7f, 0.1f, 1f);
private static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(0.3f, 0f, 1f, 1f);
@@ -140,7 +141,7 @@
float hiddenY = mWindowInsetsAnimationController.getHiddenStateInsets().bottom;
float shownY = mWindowInsetsAnimationController.getShownStateInsets().bottom;
float imeHeight = shownY - hiddenY;
- float interpolatedProgress = STANDARD_DECELERATE.getInterpolation(progress);
+ float interpolatedProgress = BACK_GESTURE.getInterpolation(progress);
int newY = (int) (imeHeight - interpolatedProgress * (imeHeight * PEEK_FRACTION));
mWindowInsetsAnimationController.setInsetsAndAlpha(Insets.of(0, 0, 0, newY), 1f,
progress);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index b52003f..1f6ceca 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -22,7 +22,6 @@
import static android.view.InsetsControllerProto.STATE;
import static android.view.InsetsSource.ID_IME;
import static android.view.InsetsSource.ID_IME_CAPTION_BAR;
-import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
import static android.view.WindowInsets.Type.FIRST;
import static android.view.WindowInsets.Type.LAST;
import static android.view.WindowInsets.Type.all;
@@ -685,9 +684,6 @@
@Override
public void onIdNotFoundInState2(int index1, InsetsSource source1) {
- if (!CAPTION_ON_SHELL && source1.getType() == captionBar()) {
- return;
- }
if (source1.getId() == ID_IME_CAPTION_BAR) {
return;
}
@@ -848,15 +844,8 @@
}
public boolean onStateChanged(InsetsState state) {
- boolean stateChanged = false;
- if (!CAPTION_ON_SHELL) {
- stateChanged = !mState.equals(state, true /* excludesCaptionBar */,
- false /* excludesInvisibleIme */)
- || captionInsetsUnchanged();
- } else {
- stateChanged = !mState.equals(state, false /* excludesCaptionBar */,
- false /* excludesInvisibleIme */);
- }
+ boolean stateChanged = !mState.equals(state, false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */);
if (!stateChanged && mLastDispatchedState.equals(state)) {
return false;
}
@@ -924,21 +913,6 @@
}
}
- private boolean captionInsetsUnchanged() {
- if (CAPTION_ON_SHELL) {
- return false;
- }
- final InsetsSource source = mState.peekSource(ID_CAPTION_BAR);
- if (source == null && mCaptionInsetsHeight == 0) {
- return false;
- }
- if (source != null && mCaptionInsetsHeight == source.getFrame().height()) {
- return false;
- }
-
- return true;
- }
-
/**
* @see InsetsState#calculateInsets(Rect, InsetsState, boolean, int, int, int, int, int,
* android.util.SparseIntArray)
@@ -1889,24 +1863,6 @@
}
@Override
- public void setCaptionInsetsHeight(int height) {
- // This method is to be removed once the caption is moved to the shell.
- if (CAPTION_ON_SHELL) {
- return;
- }
- if (mCaptionInsetsHeight != height) {
- mCaptionInsetsHeight = height;
- if (mCaptionInsetsHeight != 0) {
- mState.getOrCreateSource(ID_CAPTION_BAR, captionBar()).setFrame(
- mFrame.left, mFrame.top, mFrame.right, mFrame.top + mCaptionInsetsHeight);
- } else {
- mState.removeSource(ID_CAPTION_BAR);
- }
- mHost.notifyInsetsChanged();
- }
- }
-
- @Override
public void setImeCaptionBarInsetsHeight(int height) {
if (!ENABLE_HIDE_IME_CAPTION_BAR) {
return;
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index 00a5806..5f461c5 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -45,7 +45,6 @@
private InsetsController mReplayedInsetsController;
private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
= new ArrayList<>();
- private int mCaptionInsetsHeight = 0;
private int mImeCaptionBarInsetsHeight = 0;
private WindowInsetsAnimationControlListener mLoggingListener;
private @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible();
@@ -99,11 +98,6 @@
}
@Override
- public void setCaptionInsetsHeight(int height) {
- mCaptionInsetsHeight = height;
- }
-
- @Override
public void setImeCaptionBarInsetsHeight(int height) {
mImeCaptionBarInsetsHeight = height;
}
@@ -187,9 +181,6 @@
controller.setSystemBarsAppearanceFromResource(
mAppearanceFromResource, mAppearanceFromResourceMask);
}
- if (mCaptionInsetsHeight != 0) {
- controller.setCaptionInsetsHeight(mCaptionInsetsHeight);
- }
if (mImeCaptionBarInsetsHeight != 0) {
controller.setImeCaptionBarInsetsHeight(mImeCaptionBarInsetsHeight);
}
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 17d1404..7dc151d 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -288,7 +288,7 @@
if (bitmap == null) {
throw new IllegalArgumentException("bitmap must not be null");
}
- validateHotSpot(bitmap, hotSpotX, hotSpotY);
+ validateHotSpot(bitmap, hotSpotX, hotSpotY, false /* isScaled */);
PointerIcon icon = new PointerIcon(TYPE_CUSTOM);
icon.mBitmap = bitmap;
@@ -521,7 +521,9 @@
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
final Bitmap bitmap = getBitmapFromDrawable(bitmapDrawable);
- validateHotSpot(bitmap, hotSpotX, hotSpotY);
+ // The bitmap and hotspot are loaded from the context, which means it is implicitly scaled
+ // to the current display density, so treat this as a scaled icon when verifying hotspot.
+ validateHotSpot(bitmap, hotSpotX, hotSpotY, true /* isScaled */);
// Set the properties now that we have successfully loaded the icon.
mBitmap = bitmap;
mHotSpotX = hotSpotX;
@@ -535,11 +537,16 @@
+ ", hotspotX=" + mHotSpotX + ", hotspotY=" + mHotSpotY + "}";
}
- private static void validateHotSpot(Bitmap bitmap, float hotSpotX, float hotSpotY) {
- if (hotSpotX < 0 || hotSpotX >= bitmap.getWidth()) {
+ private static void validateHotSpot(Bitmap bitmap, float hotSpotX, float hotSpotY,
+ boolean isScaled) {
+ // Be more lenient when checking the hotspot for scaled icons to account for the restriction
+ // that bitmaps must have an integer size.
+ if (hotSpotX < 0 || (isScaled ? (int) hotSpotX > bitmap.getWidth()
+ : hotSpotX >= bitmap.getWidth())) {
throw new IllegalArgumentException("x hotspot lies outside of the bitmap area");
}
- if (hotSpotY < 0 || hotSpotY >= bitmap.getHeight()) {
+ if (hotSpotY < 0 || (isScaled ? (int) hotSpotY > bitmap.getHeight()
+ : hotSpotY >= bitmap.getHeight())) {
throw new IllegalArgumentException("y hotspot lies outside of the bitmap area");
}
}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 188ad8f..56edfe72 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -103,9 +103,9 @@
long nativeObject, float frameRate, int compatibility, int changeFrameRateStrategy);
private static native void nativeDestroy(long nativeObject);
- // 5MB is a wild guess for what the average surface should be. On most new phones, a full-screen
- // surface is about 9MB... but not all surfaces are screen size. This should be a nice balance.
- private static final long SURFACE_NATIVE_ALLOCATION_SIZE_BYTES = 5_000_000;
+ // 5KB is a balanced guess, since these are still pretty heavyweight objects, but if we make
+ // this too big, it can overwhelm the GC.
+ private static final long SURFACE_NATIVE_ALLOCATION_SIZE_BYTES = 5_000;
public static final @android.annotation.NonNull Parcelable.Creator<Surface> CREATOR =
new Parcelable.Creator<Surface>() {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index c95d6ff..a23df79 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -951,7 +951,7 @@
private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
boolean creating, boolean sizeChanged, boolean hintChanged, boolean relativeZChanged,
- Transaction surfaceUpdateTransaction) {
+ boolean hdrHeadroomChanged, Transaction surfaceUpdateTransaction) {
boolean realSizeChanged = false;
mSurfaceLock.lock();
@@ -986,7 +986,7 @@
updateBackgroundVisibility(surfaceUpdateTransaction);
updateBackgroundColor(surfaceUpdateTransaction);
- if (mLimitedHdrEnabled) {
+ if (mLimitedHdrEnabled && hdrHeadroomChanged) {
surfaceUpdateTransaction.setDesiredHdrHeadroom(
mBlastSurfaceControl, mHdrHeadroom);
}
@@ -1203,7 +1203,7 @@
}
final boolean realSizeChanged = performSurfaceTransaction(viewRoot, translator,
- creating, sizeChanged, hintChanged, relativeZChanged,
+ creating, sizeChanged, hintChanged, relativeZChanged, hdrHeadroomChanged,
surfaceUpdateTransaction);
try {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index eb7de93..ac1f646 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -43,6 +43,7 @@
import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly;
import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly;
import static android.view.flags.Flags.toolkitFrameRateSmallUsesPercentReadOnly;
+import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly;
import static android.view.flags.Flags.toolkitFrameRateViewEnablingReadOnly;
import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
@@ -2427,6 +2428,26 @@
*/
public static final int FRAME_RATE_CATEGORY_REASON_IDLE = 0x0700_0000;
+ /**
+ * This indicates that the frame rate category was chosen because it is currently boosting.
+ * @hide
+ */
+ public static final int FRAME_RATE_CATEGORY_REASON_BOOST = 0x0800_0000;
+
+ /**
+ * This indicates that the frame rate category was chosen because it is currently having
+ * touch boost.
+ * @hide
+ */
+ public static final int FRAME_RATE_CATEGORY_REASON_TOUCH = 0x0900_0000;
+
+ /**
+ * This indicates that the frame rate category was chosen because it is currently having
+ * touch boost.
+ * @hide
+ */
+ public static final int FRAME_RATE_CATEGORY_REASON_CONFLICTED = 0x0A00_0000;
+
private static final int FRAME_RATE_CATEGORY_REASON_MASK = 0xFFFF_0000;
/**
@@ -2443,6 +2464,8 @@
toolkitFrameRateSmallUsesPercentReadOnly();
private static final boolean sToolkitFrameRateViewEnablingReadOnlyFlagValue =
toolkitFrameRateViewEnablingReadOnly();
+ private static boolean sToolkitFrameRateVelocityMappingReadOnlyFlagValue =
+ toolkitFrameRateVelocityMappingReadOnly();
// Used to set frame rate compatibility.
@Surface.FrameRateCompatibility int mFrameRateCompatibility =
@@ -5739,6 +5762,8 @@
*/
private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f;
+ static final float MAX_FRAME_RATE = 140;
+
private static final int INFREQUENT_UPDATE_INTERVAL_MILLIS = 100;
private static final int INFREQUENT_UPDATE_COUNTS = 2;
@@ -20824,12 +20849,6 @@
return;
}
- // For VRR to vote the preferred frame rate
- if (sToolkitSetFrameRateReadOnlyFlagValue
- && sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
- votePreferredFrameRate();
- }
-
// Reset content capture caches
mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
mContentCaptureSessionCached = false;
@@ -20932,11 +20951,6 @@
*/
protected void damageInParent() {
if (mParent != null && mAttachInfo != null) {
- // For VRR to vote the preferred frame rate
- if (sToolkitSetFrameRateReadOnlyFlagValue
- && sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
- votePreferredFrameRate();
- }
mParent.onDescendantInvalidated(this, this);
}
}
@@ -23624,12 +23638,15 @@
return renderNode;
}
- mPrivateFlags4 = (mPrivateFlags4 & ~PFLAG4_HAS_MOVED) | PFLAG4_HAS_DRAWN;
+ // For VRR to vote the preferred frame rate
if (sToolkitSetFrameRateReadOnlyFlagValue
&& sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
+ votePreferredFrameRate();
updateInfrequentCount();
}
+ mPrivateFlags4 = (mPrivateFlags4 & ~PFLAG4_HAS_MOVED) | PFLAG4_HAS_DRAWN;
+
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
|| !renderNode.hasDisplayList()
|| (mRecreateDisplayList)) {
@@ -23694,6 +23711,8 @@
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
+
+ mFrameContentVelocity = -1;
return renderNode;
}
@@ -24792,8 +24811,6 @@
final int privateFlags = mPrivateFlags;
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
- mFrameContentVelocity = -1;
-
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
@@ -33888,38 +33905,53 @@
return mLastFrameRateCategory;
}
- private void votePreferredFrameRate() {
+ /**
+ * Used to vote the preferred frame rate and frame rate category to ViewRootImpl
+ *
+ * @hide
+ */
+ protected void votePreferredFrameRate() {
// use toolkitSetFrameRate flag to gate the change
ViewRootImpl viewRootImpl = getViewRootImpl();
int width = mRight - mLeft;
int height = mBottom - mTop;
- float alpha = mTransformationInfo != null ? mTransformationInfo.mAlpha : 1;
- int visibility = mViewFlags & VISIBILITY_MASK;
- if (viewRootImpl != null && (width != 0 && height != 0)
- && alpha != 0 && visibility == View.VISIBLE
- ) {
- if (mAttachInfo.mViewVelocityApi) {
- float velocity = mFrameContentVelocity;
- int mask = PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN;
- if (velocity < 0f && (mPrivateFlags4 & mask) == mask) {
- // This current calculation is very simple. If something on the screen moved,
- // then it votes for the highest velocity. If it doesn't move, then return 0.
- velocity = Float.POSITIVE_INFINITY;
+ if (viewRootImpl != null && (width != 0 && height != 0)) {
+ if (viewRootImpl.shouldCheckFrameRate(mPreferredFrameRate > 0f)) {
+ float velocityFrameRate = 0f;
+ if (mAttachInfo.mViewVelocityApi) {
+ float velocity = mFrameContentVelocity;
+
+ if (velocity < 0f
+ && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == (
+ PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)
+ && mParent instanceof View
+ && ((View) mParent).mFrameContentVelocity <= 0
+ ) {
+ // This current calculation is very simple. If something on the screen
+ // moved, then it votes for the highest velocity.
+ velocityFrameRate = MAX_FRAME_RATE;
+ } else if (velocity > 0f) {
+ velocityFrameRate = convertVelocityToFrameRate(velocity);
+ }
}
- if (velocity > 0f) {
- float frameRate = convertVelocityToFrameRate(velocity);
- viewRootImpl.votePreferredFrameRate(frameRate, FRAME_RATE_COMPATIBILITY_GTE);
- return;
+ if (velocityFrameRate > 0f || mPreferredFrameRate > 0f) {
+ int compatibility = FRAME_RATE_COMPATIBILITY_GTE;
+ float frameRate = velocityFrameRate;
+ if (mPreferredFrameRate > velocityFrameRate) {
+ compatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+ frameRate = mPreferredFrameRate;
+ }
+ viewRootImpl.votePreferredFrameRate(frameRate, compatibility);
}
}
- if (!willNotDraw()) {
+ if (!willNotDraw() && isDirty() && viewRootImpl.shouldCheckFrameRateCategory()) {
if (sToolkitMetricsForFrameRateDecisionFlagValue) {
float sizePercentage = width * height / mAttachInfo.mDisplayPixelCount;
viewRootImpl.recordViewPercentage(sizePercentage);
}
- int frameRateCategory;
+ int frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
if (Float.isNaN(mPreferredFrameRate)) {
frameRateCategory = calculateFrameRateCategory();
} else if (mPreferredFrameRate < 0) {
@@ -33944,10 +33976,6 @@
| FRAME_RATE_CATEGORY_REASON_INVALID;
}
}
- } else {
- viewRootImpl.votePreferredFrameRate(mPreferredFrameRate,
- mFrameRateCompatibility);
- return;
}
int category = frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK;
@@ -33962,7 +33990,7 @@
float density = mAttachInfo.mDensity;
float velocityDps = velocityPps / density;
// Choose a frame rate in increments of 10fps
- return Math.min(140f, 60f + (10f * (float) Math.floor(velocityDps / 300f)));
+ return Math.min(MAX_FRAME_RATE, 60f + (10f * (float) Math.floor(velocityDps / 300f)));
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c5a4d67..40366c2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -27,6 +27,7 @@
import static android.view.DragEvent.ACTION_DRAG_LOCATION;
import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsSource.ID_IME;
+import static android.view.Surface.FRAME_RATE_CATEGORY_DEFAULT;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
@@ -34,14 +35,18 @@
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
+import static android.view.View.FRAME_RATE_CATEGORY_REASON_BOOST;
+import static android.view.View.FRAME_RATE_CATEGORY_REASON_CONFLICTED;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_IDLE;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_INVALID;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_LARGE;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_REQUESTED;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_SMALL;
+import static android.view.View.FRAME_RATE_CATEGORY_REASON_TOUCH;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_UNKNOWN;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_VELOCITY;
+import static android.view.View.MAX_FRAME_RATE;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -234,6 +239,7 @@
import android.view.autofill.AutofillManager;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
+import android.view.flags.Flags;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
@@ -257,7 +263,6 @@
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
-import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneFallbackEventHandler;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
@@ -335,13 +340,6 @@
private static final boolean USE_ASYNC_PERFORM_HAPTIC_FEEDBACK = true;
/**
- * Whether the caption is drawn by the shell.
- * @hide
- */
- public static final boolean CAPTION_ON_SHELL =
- SystemProperties.getBoolean("persist.wm.debug.caption_on_shell", true);
-
- /**
* Whether the client (system UI) is handling the transient gesture and the corresponding
* animation.
* @hide
@@ -1049,10 +1047,10 @@
// The preferred frame rate category of the view that
// could be updated on a frame-by-frame basis.
- private int mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ private int mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_DEFAULT;
// The preferred frame rate category of the last frame that
// could be used to lower frame rate after touch boost
- private int mLastPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ private int mLastPreferredFrameRateCategory = FRAME_RATE_CATEGORY_DEFAULT;
// The preferred frame rate of the view that is mainly used for
// touch boosting, view velocity handling, and TextureView.
private float mPreferredFrameRate = 0;
@@ -1156,7 +1154,9 @@
private static boolean sToolkitFrameRateTypingReadOnlyFlagValue;
private static final boolean sToolkitFrameRateViewEnablingReadOnlyFlagValue;
private static boolean sToolkitFrameRateVelocityMappingReadOnlyFlagValue =
- toolkitFrameRateVelocityMappingReadOnly();;
+ toolkitFrameRateVelocityMappingReadOnly();
+ private static boolean sToolkitEnableInvalidateCheckThreadFlagValue =
+ Flags.enableInvalidateCheckThread();
static {
sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
@@ -2378,8 +2378,9 @@
@Override
public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {
- // TODO: Re-enable after camera is fixed or consider targetSdk checking this
- // checkThread();
+ if (sToolkitEnableInvalidateCheckThreadFlagValue) {
+ checkThread();
+ }
if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
mIsAnimating = true;
}
@@ -3176,22 +3177,6 @@
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
- private boolean updateCaptionInsets() {
- if (CAPTION_ON_SHELL) {
- return false;
- }
- if (!(mView instanceof DecorView)) return false;
- final int captionInsetsHeight = ((DecorView) mView).getCaptionInsetsHeight();
- final Rect captionFrame = new Rect();
- if (captionInsetsHeight != 0) {
- captionFrame.set(mWinFrame.left, mWinFrame.top, mWinFrame.right,
- mWinFrame.top + captionInsetsHeight);
- }
- if (mAttachInfo.mCaptionInsets.equals(captionFrame)) return false;
- mAttachInfo.mCaptionInsets.set(captionFrame);
- return true;
- }
-
private boolean shouldDispatchCutout() {
return mWindowAttributes.layoutInDisplayCutoutMode
== LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
@@ -3645,9 +3630,6 @@
mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars;
dispatchApplyInsets = true;
}
- if (updateCaptionInsets()) {
- dispatchApplyInsets = true;
- }
if (dispatchApplyInsets || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested) {
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
@@ -4217,17 +4199,25 @@
// For the variable refresh rate project.
// We set the preferred frame rate and frame rate category at the end of performTraversals
// when the values are applicable.
+ setCategoryFromCategoryCounts();
setPreferredFrameRate(mPreferredFrameRate);
setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ if (!mIsFrameRateConflicted) {
+ mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+ mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
+ FRAME_RATE_SETTING_REEVALUATE_TIME);
+ }
+ checkIdleness();
mFrameRateCategoryHighCount = mFrameRateCategoryHighCount > 0
? mFrameRateCategoryHighCount - 1 : mFrameRateCategoryHighCount;
mFrameRateCategoryNormalCount = mFrameRateCategoryNormalCount > 0
? mFrameRateCategoryNormalCount - 1 : mFrameRateCategoryNormalCount;
mFrameRateCategoryLowCount = mFrameRateCategoryLowCount > 0
? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount;
- mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_DEFAULT;
mPreferredFrameRate = -1;
mIsFrameRateConflicted = false;
+ mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN;
}
private void createSyncIfNeeded() {
@@ -6098,6 +6088,11 @@
mAccessibilityInteractionConnectionManager.ensureNoConnection();
mAccessibilityInteractionConnectionManager.ensureNoDirectConnection();
removeSendWindowContentChangedCallback();
+ if (android.view.accessibility.Flags.preventLeakingViewrootimpl()
+ && mAccessibilityInteractionController != null) {
+ mAccessibilityInteractionController.destroy();
+ mAccessibilityInteractionController = null;
+ }
destroyHardwareRenderer();
@@ -6654,8 +6649,6 @@
*/
mIsFrameRateBoosting = false;
mIsTouchBoosting = false;
- setPreferredFrameRateCategory(Math.max(mPreferredFrameRateCategory,
- mLastPreferredFrameRateCategory));
break;
case MSG_CHECK_INVALIDATION_IDLE:
if (!mHasInvalidation && !mIsFrameRateBoosting && !mIsTouchBoosting) {
@@ -7701,7 +7694,6 @@
mWindowAttributes.type)) {
// set the frame rate to the maximum value.
mIsTouchBoosting = true;
- setPreferredFrameRateCategory(mPreferredFrameRateCategory);
}
/**
* We want to lower the refresh rate when MotionEvent.ACTION_UP,
@@ -12493,57 +12485,50 @@
EventLog.writeEvent(LOGTAG_VIEWROOT_DRAW_EVENT, mTag, msg);
}
+ /**
+ * Sets the mPreferredFrameRateCategory from the high, high_hint, normal, and low counts.
+ */
+ private void setCategoryFromCategoryCounts() {
+ if (mFrameRateCategoryHighCount > 0) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
+ } else if (mFrameRateCategoryHighHintCount > 0) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT;
+ } else if (mFrameRateCategoryNormalCount > 0) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL;
+ } else if (mFrameRateCategoryLowCount > 0) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW;
+ }
+ }
+
private void setPreferredFrameRateCategory(int preferredFrameRateCategory) {
- if (!shouldSetFrameRateCategory()
- || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE
- && sToolkitFrameRateVelocityMappingReadOnlyFlagValue)) {
+ if (!shouldSetFrameRateCategory()) {
return;
}
- int categoryFromConflictedFrameRates = FRAME_RATE_CATEGORY_NO_PREFERENCE;
- if (mIsFrameRateConflicted) {
- categoryFromConflictedFrameRates = mPreferredFrameRate > 60
- ? FRAME_RATE_CATEGORY_HIGH : FRAME_RATE_CATEGORY_NORMAL;
- }
- int frameRateCategory = mIsTouchBoosting
- ? FRAME_RATE_CATEGORY_HIGH_HINT
- : Math.max(preferredFrameRateCategory, categoryFromConflictedFrameRates);
+ int frameRateCategory;
+ int frameRateReason;
+ String view;
- // FRAME_RATE_CATEGORY_HIGH has a higher precedence than FRAME_RATE_CATEGORY_HIGH_HINT
- // For now, FRAME_RATE_CATEGORY_HIGH_HINT is used for boosting with user interaction.
- // FRAME_RATE_CATEGORY_HIGH is for boosting without user interaction
- // (e.g., Window Initialization).
- if (mIsFrameRateBoosting || mInsetsAnimationRunning
- || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE
- && mPreferredFrameRate > 0)) {
+ if (mIsFrameRateBoosting || mInsetsAnimationRunning) {
frameRateCategory = FRAME_RATE_CATEGORY_HIGH;
- if (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) {
- // We've received a velocity, so we'll let the velocity control the
- // frame rate unless we receive additional motion events.
- mIsTouchBoosting = false;
- mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY;
- mFrameRateCategoryView = null;
- } else {
- mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN;
- }
+ frameRateReason = FRAME_RATE_CATEGORY_REASON_BOOST;
+ view = null;
+ } else if (mIsTouchBoosting && preferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH_HINT) {
+ frameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT;
+ frameRateReason = FRAME_RATE_CATEGORY_REASON_TOUCH;
+ view = null;
+ } else {
+ frameRateCategory = preferredFrameRateCategory;
+ frameRateReason = mFrameRateCategoryChangeReason;
+ view = mFrameRateCategoryView;
}
try {
- if (mLastPreferredFrameRateCategory != frameRateCategory) {
+ if (frameRateCategory != FRAME_RATE_CATEGORY_DEFAULT
+ && mLastPreferredFrameRateCategory != frameRateCategory) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
- String reason = reasonToString(mFrameRateCategoryChangeReason);
- String sourceView = mFrameRateCategoryView == null ? "-"
- : mFrameRateCategoryView;
- if (preferredFrameRateCategory == FRAME_RATE_CATEGORY_HIGH_HINT) {
- reason = "touch boost";
- sourceView = "-";
- } else if (categoryFromConflictedFrameRates == frameRateCategory
- && frameRateCategory != preferredFrameRateCategory
- && mIsFrameRateConflicted
- ) {
- reason = "conflict";
- sourceView = "-";
- }
+ String reason = reasonToString(frameRateReason);
+ String sourceView = view == null ? "-" : view;
String category = categoryToString(frameRateCategory);
Trace.traceBegin(
Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRateCategory "
@@ -12587,24 +12572,21 @@
case FRAME_RATE_CATEGORY_REASON_VELOCITY -> str = "velocity";
case FRAME_RATE_CATEGORY_REASON_IDLE -> str = "idle";
case FRAME_RATE_CATEGORY_REASON_UNKNOWN -> str = "unknown";
+ case FRAME_RATE_CATEGORY_REASON_BOOST -> str = "boost";
+ case FRAME_RATE_CATEGORY_REASON_TOUCH -> str = "touch";
+ case FRAME_RATE_CATEGORY_REASON_CONFLICTED -> str = "conflicted";
default -> str = String.valueOf(reason);
}
return str;
}
private void setPreferredFrameRate(float preferredFrameRate) {
- if (!shouldSetFrameRate()) {
- return;
- }
- if (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE
- && preferredFrameRate > 0 && !sToolkitFrameRateVelocityMappingReadOnlyFlagValue) {
- mIsTouchBoosting = false;
+ if (!shouldSetFrameRate() || preferredFrameRate < 0) {
return;
}
try {
- if (mLastPreferredFrameRate != preferredFrameRate
- && preferredFrameRate >= 0) {
+ if (mLastPreferredFrameRate != preferredFrameRate) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(
Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRate "
@@ -12613,7 +12595,7 @@
}
if (sToolkitFrameRateFunctionEnablingReadOnlyFlagValue) {
mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate,
- mFrameRateCompatibility).applyAsyncUnsafe();
+ mFrameRateCompatibility).applyAsyncUnsafe();
}
mLastPreferredFrameRate = preferredFrameRate;
}
@@ -12624,12 +12606,6 @@
}
}
- private void sendDelayedEmptyMessage(int message, int delayedTime) {
- mHandler.removeMessages(message);
-
- mHandler.sendEmptyMessageDelayed(message, delayedTime);
- }
-
private boolean shouldSetFrameRateCategory() {
// use toolkitSetFrameRate flag to gate the change
return mSurface.isValid() && shouldEnableDvrr();
@@ -12667,25 +12643,34 @@
case FRAME_RATE_CATEGORY_HIGH ->
mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
}
-
- int oldCategory = mPreferredFrameRateCategory;
- if (mFrameRateCategoryHighCount > 0) {
- mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
- } else if (mFrameRateCategoryHighHintCount > 0) {
- mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT;
- } else if (mFrameRateCategoryNormalCount > 0) {
- mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL;
- } else if (mFrameRateCategoryLowCount > 0) {
- mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW;
+ if (frameRateCategory > mPreferredFrameRateCategory) {
+ mPreferredFrameRateCategory = frameRateCategory;
+ mFrameRateCategoryChangeReason = reason;
+ mFrameRateCategoryView = view == null ? "-" : view.getClass().getSimpleName();
}
mHasInvalidation = true;
- checkIdleness();
- if (mPreferredFrameRateCategory != oldCategory
- && mPreferredFrameRateCategory == frameRateCategory
- ) {
- mFrameRateCategoryChangeReason = reason;
- mFrameRateCategoryView = view == null ? "null" : view.getClass().getSimpleName();
- }
+ }
+
+ /**
+ * Returns whether a View should vote for frame rate category. When the category is HIGH
+ * already, there's no need to calculate the category on the View and vote.
+ */
+ public boolean shouldCheckFrameRateCategory() {
+ return mPreferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH;
+ }
+
+ /**
+ * Returns whether a View should vote for frame rate. When the maximum frame rate has already
+ * been voted for, there's no point in calculating and voting for the frame rate. When
+ * isDirect is false, then it will return false when the velocity-calculated frame rate
+ * can be avoided.
+ * @param isDirect true when the frame rate has been set directly on the View or false if
+ * the calculation is based only on velocity.
+ */
+ public boolean shouldCheckFrameRate(boolean isDirect) {
+ return mPreferredFrameRate < MAX_FRAME_RATE
+ || (!isDirect && !sToolkitFrameRateVelocityMappingReadOnlyFlagValue
+ && mPreferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH);
}
/**
@@ -12711,24 +12696,44 @@
if (frameRate <= 0) {
return;
}
+ if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) {
+ mIsTouchBoosting = false;
+ if (!sToolkitFrameRateVelocityMappingReadOnlyFlagValue) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
+ mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
+ mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY;
+ mFrameRateCategoryView = null;
+ return;
+ }
+ }
+ float nextFrameRate;
+ int nextFrameRateCompatibility;
+ if (frameRate > mPreferredFrameRate) {
+ nextFrameRate = frameRate;
+ nextFrameRateCompatibility = frameRateCompatibility;
+ } else {
+ nextFrameRate = mPreferredFrameRate;
+ nextFrameRateCompatibility = mFrameRateCompatibility;
+ }
+
if (mPreferredFrameRate > 0 && mPreferredFrameRate % frameRate != 0
&& frameRate % mPreferredFrameRate != 0) {
mIsFrameRateConflicted = true;
- mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
- }
- if (frameRate > mPreferredFrameRate) {
- mFrameRateCompatibility = frameRateCompatibility;
+ if (nextFrameRate > 60 && mFrameRateCategoryHighCount != FRAME_RATE_CATEGORY_COUNT) {
+ mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
+ mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_CONFLICTED;
+ mFrameRateCategoryView = null;
+ } else if (mFrameRateCategoryHighCount == 0 && mFrameRateCategoryHighHintCount == 0
+ && mFrameRateCategoryNormalCount < FRAME_RATE_CATEGORY_COUNT) {
+ mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT;
+ mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_CONFLICTED;
+ mFrameRateCategoryView = null;
+ }
}
- mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate);
+ mPreferredFrameRate = nextFrameRate;
+ mFrameRateCompatibility = nextFrameRateCompatibility;
mHasInvalidation = true;
-
- if (!mIsFrameRateConflicted) {
- mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
- mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
- FRAME_RATE_SETTING_REEVALUATE_TIME);
- }
- checkIdleness();
}
/**
@@ -12798,7 +12803,6 @@
private void boostFrameRate(int boostTimeOut) {
mIsFrameRateBoosting = true;
- setPreferredFrameRateCategory(mPreferredFrameRateCategory);
mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT,
boostTimeOut);
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 1ffffb3..19c98a2 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -284,15 +284,6 @@
@Appearance int getSystemBarsAppearance();
/**
- * Notify the caption insets height change. The information will be used on the client side to,
- * make sure the InsetsState has the correct caption insets.
- *
- * @param height the height of caption bar insets.
- * @hide
- */
- void setCaptionInsetsHeight(int height);
-
- /**
* Sets the insets height for the IME caption bar, which corresponds to the
* "fake" IME navigation bar.
*
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 59cb450..afe5b7e 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1565,8 +1565,9 @@
* android:value="true|false"/>
* </activity>
* </pre>
+ *
+ * @hide
*/
- @FlaggedApi(Flags.FLAG_UNTRUSTED_EMBEDDING_STATE_SHARING)
String PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING =
"android.window.PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING";
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 12ce0f4..bdfc236 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -27,6 +27,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresNoPermission;
import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -1190,6 +1191,8 @@
/**
* {@inheritDoc}
*/
+ @Override
+ @RequiresNoPermission
public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
int interactionId) {
synchronized (mInstanceLock) {
@@ -1231,6 +1234,8 @@
/**
* {@inheritDoc}
*/
+ @Override
+ @RequiresNoPermission
public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
int interactionId) {
synchronized (mInstanceLock) {
@@ -1260,6 +1265,7 @@
* {@inheritDoc}
*/
@Override
+ @RequiresNoPermission
public void setPrefetchAccessibilityNodeInfoResult(@NonNull List<AccessibilityNodeInfo> infos,
int interactionId) {
int interactionIdWaitingForPrefetchResultCopy = -1;
@@ -1324,6 +1330,8 @@
/**
* {@inheritDoc}
*/
+ @Override
+ @RequiresNoPermission
public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
synchronized (mInstanceLock) {
if (interactionId > mInteractionId) {
@@ -1372,6 +1380,7 @@
* @param interactionId The interaction id of the request.
*/
@Override
+ @RequiresNoPermission
public void sendTakeScreenshotOfWindowError(
@AccessibilityService.ScreenshotErrorCode int errorCode, int interactionId) {
synchronized (mInstanceLock) {
@@ -1729,6 +1738,7 @@
* @param interactionId The interaction id of the request.
*/
@Override
+ @RequiresNoPermission
public void sendAttachOverlayResult(
@AccessibilityService.AttachOverlayResult int result, int interactionId) {
if (!Flags.a11yOverlayCallbacks()) {
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
index fe59519..a9e5db5 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
@@ -34,6 +34,7 @@
* @param interactionId The interaction id to match the result with the request.
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+ @RequiresNoPermission
void setFindAccessibilityNodeInfoResult(in AccessibilityNodeInfo info, int interactionId);
/**
@@ -43,6 +44,7 @@
* @param interactionId The interaction id to match the result with the request.
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+ @RequiresNoPermission
void setFindAccessibilityNodeInfosResult(in List<AccessibilityNodeInfo> infos,
int interactionId);
@@ -52,6 +54,7 @@
* @param root The {@link AccessibilityNodeInfo} for which the prefetching is based off of.
* @param infos The result {@link AccessibilityNodeInfo}s.
*/
+ @RequiresNoPermission
void setPrefetchAccessibilityNodeInfoResult(
in List<AccessibilityNodeInfo> infos, int interactionId);
@@ -62,15 +65,18 @@
* @param interactionId The interaction id to match the result with the request.
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+ @RequiresNoPermission
void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
/**
* Sends an error code for a window screenshot request to the requesting client.
*/
+ @RequiresNoPermission
void sendTakeScreenshotOfWindowError(int errorCode, int interactionId);
/**
* Sends an result code for an attach overlay request to the requesting client.
*/
+ @RequiresNoPermission
void sendAttachOverlayResult(int result, int interactionId);
}
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index eefc72b..c9d99d1 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -80,6 +80,16 @@
}
flag {
+ name: "migrate_enable_shortcuts"
+ namespace: "accessibility"
+ description: "Refactors deprecated code to use AccessibilityManager#enableShortcutsForTargets."
+ bug: "332006721"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "motion_event_observing"
is_exported: true
namespace: "accessibility"
@@ -132,6 +142,16 @@
}
flag {
+ name: "prevent_leaking_viewrootimpl"
+ namespace: "accessibility"
+ description: "Clear pending messages and callbacks of the handler in AccessibilityInteractionController when the ViewRootImpl is detached from Window to prevent leaking ViewRootImpl"
+ bug: "320701910"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "support_system_pinch_zoom_opt_out_apis"
namespace: "accessibility"
description: "Feature flag for declaring system pinch zoom opt-out apis"
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/core/java/android/view/animation/BackGestureInterpolator.java
similarity index 68%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to core/java/android/view/animation/BackGestureInterpolator.java
index f6140f5..c1595db 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/core/java/android/view/animation/BackGestureInterpolator.java
@@ -14,9 +14,13 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
-
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+package android.view.animation;
+/**
+ * Decelerating interpolator with a very slight acceleration phase at the beginning.
+ * @hide
+ */
+public class BackGestureInterpolator extends PathInterpolator {
+ public BackGestureInterpolator() {
+ super(0.1f, 0.1f, 0f, 1f);
+ }
+}
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index c482f8b..486c2ab 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -50,3 +50,11 @@
description: "Enable default arrow icon when hovering on buttons or clickable widgets."
bug: "299269803"
}
+
+flag {
+ name: "enable_invalidate_check_thread"
+ namespace: "toolkit"
+ description: "Enable checkThread call in ViewRootImpl#onDescendentInvalidated"
+ bug: "333752000"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 8f1b72e..1034479 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -40,6 +40,7 @@
import android.util.AndroidRuntimeException;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
import java.io.File;
import java.lang.reflect.Method;
@@ -609,7 +610,7 @@
startedRelroProcesses = WebViewLibraryLoader.prepareNativeLibraries(packageInfo);
} catch (Throwable t) {
// Log and discard errors at this stage as we must not crash the system server.
- Log.e(LOGTAG, "error preparing webview native library", t);
+ Slog.wtf(LOGTAG, "error preparing webview native library", t);
}
WebViewZygote.onWebViewProviderChanged(packageInfo);
diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
index a68a577..1a8745c 100644
--- a/core/java/android/webkit/WebViewLibraryLoader.java
+++ b/core/java/android/webkit/WebViewLibraryLoader.java
@@ -25,6 +25,7 @@
import android.os.Build;
import android.os.Process;
import android.util.Log;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
@@ -137,7 +138,7 @@
if (!success) throw new Exception("Failed to start the relro file creator process");
} catch (Throwable t) {
// Log and discard errors as we must not crash the system server.
- Log.e(LOGTAG, "error starting relro file creator for abi " + abi, t);
+ Slog.wtf(LOGTAG, "error starting relro file creator for abi " + abi, t);
crashHandler.run();
}
}
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 65984f5..ead8887 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -45,8 +45,10 @@
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.IAccessibilityManager;
+import android.widget.flags.Flags;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -205,27 +207,41 @@
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
- tn.mNextView = new WeakReference<>(mNextView);
+ if (Flags.toastNoWeakref()) {
+ tn.mNextView = mNextView;
+ } else {
+ tn.mNextViewWeakRef = new WeakReference<>(mNextView);
+ }
final boolean isUiContext = mContext.isUiContext();
final int displayId = mContext.getDisplayId();
+ boolean wasEnqueued = false;
try {
if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
if (mNextView != null) {
// It's a custom toast
- service.enqueueToast(pkg, mToken, tn, mDuration, isUiContext, displayId);
+ wasEnqueued = service.enqueueToast(pkg, mToken, tn, mDuration, isUiContext,
+ displayId);
} else {
// It's a text toast
ITransientNotificationCallback callback =
new CallbackBinder(mCallbacks, mHandler);
- service.enqueueTextToast(pkg, mToken, mText, mDuration, isUiContext, displayId,
- callback);
+ wasEnqueued = service.enqueueTextToast(pkg, mToken, mText, mDuration,
+ isUiContext, displayId, callback);
}
} else {
- service.enqueueToast(pkg, mToken, tn, mDuration, isUiContext, displayId);
+ wasEnqueued = service.enqueueToast(pkg, mToken, tn, mDuration, isUiContext,
+ displayId);
}
} catch (RemoteException e) {
// Empty
+ } finally {
+ if (Flags.toastNoWeakref()) {
+ if (!wasEnqueued) {
+ tn.mNextViewWeakRef = null;
+ tn.mNextView = null;
+ }
+ }
}
}
@@ -581,6 +597,16 @@
}
}
+ /**
+ * Get the Toast.TN ITransientNotification object
+ * @return TN
+ * @hide
+ */
+ @VisibleForTesting
+ public TN getTn() {
+ return mTN;
+ }
+
// =======================================================================================
// All the gunk below is the interaction with the Notification Service, which handles
// the proper ordering of these system-wide.
@@ -599,7 +625,11 @@
return sService;
}
- private static class TN extends ITransientNotification.Stub {
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static class TN extends ITransientNotification.Stub {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private final WindowManager.LayoutParams mParams;
@@ -620,7 +650,9 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
View mView;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- WeakReference<View> mNextView;
+ WeakReference<View> mNextViewWeakRef;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ View mNextView;
int mDuration;
WindowManager mWM;
@@ -662,14 +694,22 @@
handleHide();
// Don't do this in handleHide() because it is also invoked by
// handleShow()
- mNextView = null;
+ if (Flags.toastNoWeakref()) {
+ mNextView = null;
+ } else {
+ mNextViewWeakRef = null;
+ }
break;
}
case CANCEL: {
handleHide();
// Don't do this in handleHide() because it is also invoked by
// handleShow()
- mNextView = null;
+ if (Flags.toastNoWeakref()) {
+ mNextView = null;
+ } else {
+ mNextViewWeakRef = null;
+ }
try {
getService().cancelToast(mPackageName, mToken);
} catch (RemoteException e) {
@@ -716,21 +756,43 @@
}
public void handleShow(IBinder windowToken) {
- if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
- + " mNextView=" + mNextView);
+ if (Flags.toastNoWeakref()) {
+ if (localLOGV) {
+ Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ + " mNextView=" + mNextView);
+ }
+ } else {
+ if (localLOGV) {
+ Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ + " mNextView=" + mNextViewWeakRef);
+ }
+ }
// If a cancel/hide is pending - no need to show - at this point
// the window token is already invalid and no need to do any work.
if (mHandler.hasMessages(CANCEL) || mHandler.hasMessages(HIDE)) {
return;
}
- if (mNextView != null && mView != mNextView.get()) {
- // remove the old view if necessary
- handleHide();
- mView = mNextView.get();
- if (mView != null) {
- mPresenter.show(mView, mToken, windowToken, mDuration, mGravity, mX, mY,
- mHorizontalMargin, mVerticalMargin,
- new CallbackBinder(getCallbacks(), mHandler));
+ if (Flags.toastNoWeakref()) {
+ if (mNextView != null && mView != mNextView) {
+ // remove the old view if necessary
+ handleHide();
+ mView = mNextView;
+ if (mView != null) {
+ mPresenter.show(mView, mToken, windowToken, mDuration, mGravity, mX, mY,
+ mHorizontalMargin, mVerticalMargin,
+ new CallbackBinder(getCallbacks(), mHandler));
+ }
+ }
+ } else {
+ if (mNextViewWeakRef != null && mView != mNextViewWeakRef.get()) {
+ // remove the old view if necessary
+ handleHide();
+ mView = mNextViewWeakRef.get();
+ if (mView != null) {
+ mPresenter.show(mView, mToken, windowToken, mDuration, mGravity, mX, mY,
+ mHorizontalMargin, mVerticalMargin,
+ new CallbackBinder(getCallbacks(), mHandler));
+ }
}
}
}
@@ -745,6 +807,23 @@
mView = null;
}
}
+
+ /**
+ * Get the next view to show for enqueued toasts
+ * Custom toast views are deprecated.
+ * @see #setView(View)
+ *
+ * @return next view
+ * @hide
+ */
+ @VisibleForTesting
+ public View getNextView() {
+ if (Flags.toastNoWeakref()) {
+ return mNextView;
+ } else {
+ return (mNextViewWeakRef != null) ? mNextViewWeakRef.get() : null;
+ }
+ }
}
/**
diff --git a/core/java/android/widget/flags/notification_widget_flags.aconfig b/core/java/android/widget/flags/notification_widget_flags.aconfig
index e60fa15..515fa55 100644
--- a/core/java/android/widget/flags/notification_widget_flags.aconfig
+++ b/core/java/android/widget/flags/notification_widget_flags.aconfig
@@ -25,4 +25,14 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+flag {
+ name: "toast_no_weakref"
+ namespace: "systemui"
+ description: "Do not use WeakReference for custom view Toast"
+ bug: "321732224"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index 4816f35..b1cf834 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -72,8 +72,17 @@
/**
* Key to access the boolean value passed in {#mOnBackNavigationDone} result bundle
* that represents if back navigation has been triggered.
+ * @hide
*/
- public static final String KEY_TRIGGER_BACK = "TriggerBack";
+ public static final String KEY_NAVIGATION_FINISHED = "NavigationFinished";
+
+ /**
+ * Key to access the boolean value passed in {#mOnBackNavigationDone} result bundle
+ * that represents if back gesture has been triggered.
+ * @hide
+ */
+ public static final String KEY_GESTURE_FINISHED = "GestureFinished";
+
/**
* Defines the type of back destinations a back even can lead to. This is used to define the
@@ -192,7 +201,21 @@
public void onBackNavigationFinished(boolean triggerBack) {
if (mOnBackNavigationDone != null) {
Bundle result = new Bundle();
- result.putBoolean(KEY_TRIGGER_BACK, triggerBack);
+ result.putBoolean(KEY_NAVIGATION_FINISHED, triggerBack);
+ mOnBackNavigationDone.sendResult(result);
+ }
+ }
+
+ /**
+ * Callback to be called when the back gesture is finished in order to notify the server that
+ * it can ask app to start rendering.
+ * @hide
+ * @param triggerBack Boolean indicating if back gesture has been triggered.
+ */
+ public void onBackGestureFinished(boolean triggerBack) {
+ if (mOnBackNavigationDone != null) {
+ Bundle result = new Bundle();
+ result.putBoolean(KEY_GESTURE_FINISHED, triggerBack);
mOnBackNavigationDone.sendResult(result);
}
}
diff --git a/core/java/android/window/IRemoteTransition.aidl b/core/java/android/window/IRemoteTransition.aidl
index ec8b66d..33421ba 100644
--- a/core/java/android/window/IRemoteTransition.aidl
+++ b/core/java/android/window/IRemoteTransition.aidl
@@ -19,6 +19,7 @@
import android.view.SurfaceControl;
import android.window.IRemoteTransitionFinishedCallback;
import android.window.TransitionInfo;
+import android.window.WindowAnimationState;
/**
* Interface allowing remote processes to play transition animations.
@@ -61,6 +62,19 @@
in IRemoteTransitionFinishedCallback finishCallback);
/**
+ * Takes over the animation of the windows from an existing transition. Once complete, the
+ * implementation should call `finishCallback`.
+ *
+ * @param transition An identifier for the transition to be taken over.
+ * @param states The animation states of the windows involved in the transition. These must be
+ * sorted in the same way as the Changes inside `info`, and each state may be
+ * null.
+ */
+ void takeOverAnimation(in IBinder transition, in TransitionInfo info,
+ in SurfaceControl.Transaction t, in IRemoteTransitionFinishedCallback finishCallback,
+ in WindowAnimationState[] states);
+
+ /**
* Called when a different handler has consumed the transition
*
* @param transition An identifier for the transition that was consumed.
diff --git a/core/java/android/window/RemoteTransitionStub.java b/core/java/android/window/RemoteTransitionStub.java
new file mode 100644
index 0000000..4a97b1e
--- /dev/null
+++ b/core/java/android/window/RemoteTransitionStub.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.SurfaceControl;
+
+/**
+ * Utility base implementation of {@link IRemoteTransition} that users can extend to avoid stubbing.
+ *
+ * @hide
+ */
+public abstract class RemoteTransitionStub extends IRemoteTransition.Stub {
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {}
+
+
+ @Override
+ public void takeOverAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction startTransaction,
+ IRemoteTransitionFinishedCallback finishCallback,
+ WindowAnimationState[] states) throws RemoteException {
+ throw new RemoteException("Takeovers are not supported by this IRemoteTransition");
+ }
+
+
+ @Override
+ public void onTransitionConsumed(IBinder transition, boolean aborted)
+ throws RemoteException {}
+}
diff --git a/core/java/android/window/TaskFragmentCreationParams.java b/core/java/android/window/TaskFragmentCreationParams.java
index 93297e6..89327fe 100644
--- a/core/java/android/window/TaskFragmentCreationParams.java
+++ b/core/java/android/window/TaskFragmentCreationParams.java
@@ -18,10 +18,13 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.WindowingMode;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.TestApi;
+import android.content.pm.ActivityInfo.ScreenOrientation;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.Parcel;
@@ -101,11 +104,20 @@
*/
private final boolean mAllowTransitionWhenEmpty;
+ /**
+ * The override orientation for the TaskFragment. This is effective only for a system organizer.
+ * The value is ignored otherwise. Default to {@code SCREEN_ORIENTATION_UNSPECIFIED}.
+ *
+ * @see TaskFragmentOrganizer#registerOrganizer(boolean)
+ */
+ private final @ScreenOrientation int mOverrideOrientation;
+
private TaskFragmentCreationParams(
@NonNull TaskFragmentOrganizerToken organizer, @NonNull IBinder fragmentToken,
@NonNull IBinder ownerToken, @NonNull Rect initialRelativeBounds,
@WindowingMode int windowingMode, @Nullable IBinder pairedPrimaryFragmentToken,
- @Nullable IBinder pairedActivityToken, boolean allowTransitionWhenEmpty) {
+ @Nullable IBinder pairedActivityToken, boolean allowTransitionWhenEmpty,
+ @ScreenOrientation int overrideOrientation) {
if (pairedPrimaryFragmentToken != null && pairedActivityToken != null) {
throw new IllegalArgumentException("pairedPrimaryFragmentToken and"
+ " pairedActivityToken should not be set at the same time.");
@@ -118,6 +130,7 @@
mPairedPrimaryFragmentToken = pairedPrimaryFragmentToken;
mPairedActivityToken = pairedActivityToken;
mAllowTransitionWhenEmpty = allowTransitionWhenEmpty;
+ mOverrideOrientation = overrideOrientation;
}
@NonNull
@@ -168,6 +181,11 @@
return mAllowTransitionWhenEmpty;
}
+ /** @hide */
+ public @ScreenOrientation int getOverrideOrientation() {
+ return mOverrideOrientation;
+ }
+
private TaskFragmentCreationParams(Parcel in) {
mOrganizer = TaskFragmentOrganizerToken.CREATOR.createFromParcel(in);
mFragmentToken = in.readStrongBinder();
@@ -177,6 +195,7 @@
mPairedPrimaryFragmentToken = in.readStrongBinder();
mPairedActivityToken = in.readStrongBinder();
mAllowTransitionWhenEmpty = in.readBoolean();
+ mOverrideOrientation = in.readInt();
}
/** @hide */
@@ -190,6 +209,7 @@
dest.writeStrongBinder(mPairedPrimaryFragmentToken);
dest.writeStrongBinder(mPairedActivityToken);
dest.writeBoolean(mAllowTransitionWhenEmpty);
+ dest.writeInt(mOverrideOrientation);
}
@NonNull
@@ -217,6 +237,7 @@
+ " pairedFragmentToken=" + mPairedPrimaryFragmentToken
+ " pairedActivityToken=" + mPairedActivityToken
+ " allowTransitionWhenEmpty=" + mAllowTransitionWhenEmpty
+ + " overrideOrientation=" + mOverrideOrientation
+ "}";
}
@@ -252,6 +273,8 @@
private boolean mAllowTransitionWhenEmpty;
+ private @ScreenOrientation int mOverrideOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
public Builder(@NonNull TaskFragmentOrganizerToken organizer,
@NonNull IBinder fragmentToken, @NonNull IBinder ownerToken) {
mOrganizer = organizer;
@@ -330,12 +353,28 @@
return this;
}
+ /**
+ * Sets the override orientation for the TaskFragment. This is effective only for a system
+ * organizer. The value is ignored otherwise. Default to
+ * {@code SCREEN_ORIENTATION_UNSPECIFIED}.
+ *
+ * @see TaskFragmentOrganizer#registerOrganizer(boolean)
+ *
+ * @hide
+ */
+ @RequiresPermission(value = android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+ @NonNull
+ public Builder setOverrideOrientation(@ScreenOrientation int overrideOrientation) {
+ mOverrideOrientation = overrideOrientation;
+ return this;
+ }
+
/** Constructs the options to create TaskFragment with. */
@NonNull
public TaskFragmentCreationParams build() {
return new TaskFragmentCreationParams(mOrganizer, mFragmentToken, mOwnerToken,
mInitialRelativeBounds, mWindowingMode, mPairedPrimaryFragmentToken,
- mPairedActivityToken, mAllowTransitionWhenEmpty);
+ mPairedActivityToken, mAllowTransitionWhenEmpty, mOverrideOrientation);
}
}
}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 5227724..bcae571 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -523,6 +523,9 @@
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM");
}
+ if ((flags & FLAG_CONFIG_AT_END) != 0) {
+ sb.append(sb.length() == 0 ? "" : "|").append("CONFIG_AT_END");
+ }
if ((flags & FLAG_MOVED_TO_TOP) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("MOVE_TO_TOP");
}
@@ -538,6 +541,11 @@
// If the change has no parent (it is root), then it is independent
if (change.getParent() == null) return true;
+ if (change.getLastParent() != null && !change.getLastParent().equals(change.getParent())) {
+ // If the change has been reparented, then it's independent.
+ return true;
+ }
+
// non-visibility changes will just be folded into the parent change, so they aren't
// independent either.
if (change.getMode() == TRANSIT_CHANGE) return false;
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/core/java/android/window/WindowAnimationState.aidl
similarity index 60%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to core/java/android/window/WindowAnimationState.aidl
index f6140f5..e662860 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/core/java/android/window/WindowAnimationState.aidl
@@ -14,9 +14,23 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package android.window;
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import android.graphics.PointF;
+import android.graphics.RectF;
+
+/**
+ * Properties of a window animation at a given point in time.
+ *
+ * {@hide}
+ */
+parcelable WindowAnimationState {
+ long timestamp;
+ RectF bounds;
+ float scale;
+ float topLeftRadius;
+ float topRightRadius;
+ float bottomRightRadius;
+ float bottomLeftRadius;
+ PointF velocityPxPerMs;
+}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 76a34ae..4148e00 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -88,6 +88,19 @@
}
/**
+ * Clear the transaction object.
+ * This is equivalent to a new empty {@link WindowContainerTransaction} in content.
+ *
+ * @hide
+ */
+ public void clear() {
+ mChanges.clear();
+ mHierarchyOps.clear();
+ mErrorCallbackToken = null;
+ mTaskFragmentOrganizer = null;
+ }
+
+ /**
* Resize a container.
*/
@NonNull
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index bcbac93..47a4052 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -36,6 +36,8 @@
import androidx.annotation.VisibleForTesting;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -75,14 +77,17 @@
@Nullable
private ImeBackAnimationController mImeBackAnimationController;
+ @GuardedBy("mLock")
/** Convenience hashmap to quickly decide if a callback has been added. */
private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
/** Holds all callbacks by priorities. */
@VisibleForTesting
+ @GuardedBy("mLock")
public final TreeMap<Integer, ArrayList<OnBackInvokedCallback>>
mOnBackInvokedCallbacks = new TreeMap<>();
private Checker mChecker;
+ private final Object mLock = new Object();
public WindowOnBackInvokedDispatcher(@NonNull Context context) {
mChecker = new Checker(context);
@@ -94,20 +99,24 @@
*/
public void attachToWindow(@NonNull IWindowSession windowSession, @NonNull IWindow window,
@Nullable ImeBackAnimationController imeBackAnimationController) {
- mWindowSession = windowSession;
- mWindow = window;
- mImeBackAnimationController = imeBackAnimationController;
- if (!mAllCallbacks.isEmpty()) {
- setTopOnBackInvokedCallback(getTopCallback());
+ synchronized (mLock) {
+ mWindowSession = windowSession;
+ mWindow = window;
+ mImeBackAnimationController = imeBackAnimationController;
+ if (!mAllCallbacks.isEmpty()) {
+ setTopOnBackInvokedCallback(getTopCallback());
+ }
}
}
/** Detaches the dispatcher instance from its window. */
public void detachFromWindow() {
- clear();
- mWindow = null;
- mWindowSession = null;
- mImeBackAnimationController = null;
+ synchronized (mLock) {
+ clear();
+ mWindow = null;
+ mWindowSession = null;
+ mImeBackAnimationController = null;
+ }
}
// TODO: Take an Executor for the callback to run on.
@@ -125,65 +134,71 @@
*/
public void registerOnBackInvokedCallbackUnchecked(
@NonNull OnBackInvokedCallback callback, @Priority int priority) {
- if (mImeDispatcher != null) {
- mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
- return;
- }
- if (!mOnBackInvokedCallbacks.containsKey(priority)) {
- mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
- }
- if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback) {
- callback = mImeBackAnimationController;
- }
- ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
-
- // If callback has already been added, remove it and re-add it.
- if (mAllCallbacks.containsKey(callback)) {
- if (DEBUG) {
- Log.i(TAG, "Callback already added. Removing and re-adding it.");
+ synchronized (mLock) {
+ if (mImeDispatcher != null) {
+ mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
+ return;
}
- Integer prevPriority = mAllCallbacks.get(callback);
- mOnBackInvokedCallbacks.get(prevPriority).remove(callback);
- }
+ if (!mOnBackInvokedCallbacks.containsKey(priority)) {
+ mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
+ }
+ if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback) {
+ callback = mImeBackAnimationController;
+ }
+ ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
- OnBackInvokedCallback previousTopCallback = getTopCallback();
- callbacks.add(callback);
- mAllCallbacks.put(callback, priority);
- if (previousTopCallback == null
- || (previousTopCallback != callback
- && mAllCallbacks.get(previousTopCallback) <= priority)) {
- setTopOnBackInvokedCallback(callback);
+ // If callback has already been added, remove it and re-add it.
+ if (mAllCallbacks.containsKey(callback)) {
+ if (DEBUG) {
+ Log.i(TAG, "Callback already added. Removing and re-adding it.");
+ }
+ Integer prevPriority = mAllCallbacks.get(callback);
+ mOnBackInvokedCallbacks.get(prevPriority).remove(callback);
+ }
+
+ OnBackInvokedCallback previousTopCallback = getTopCallback();
+ callbacks.add(callback);
+ mAllCallbacks.put(callback, priority);
+ if (previousTopCallback == null
+ || (previousTopCallback != callback
+ && mAllCallbacks.get(previousTopCallback) <= priority)) {
+ setTopOnBackInvokedCallback(callback);
+ }
}
}
@Override
public void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
- if (mImeDispatcher != null) {
- mImeDispatcher.unregisterOnBackInvokedCallback(callback);
- return;
- }
- if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback) {
- callback = mImeBackAnimationController;
- }
- if (!mAllCallbacks.containsKey(callback)) {
- if (DEBUG) {
- Log.i(TAG, "Callback not found. returning...");
+ synchronized (mLock) {
+ if (mImeDispatcher != null) {
+ mImeDispatcher.unregisterOnBackInvokedCallback(callback);
+ return;
}
- return;
- }
- OnBackInvokedCallback previousTopCallback = getTopCallback();
- Integer priority = mAllCallbacks.get(callback);
- ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
- callbacks.remove(callback);
- if (callbacks.isEmpty()) {
- mOnBackInvokedCallbacks.remove(priority);
- }
- mAllCallbacks.remove(callback);
- // Re-populate the top callback to WM if the removed callback was previously the top one.
- if (previousTopCallback == callback) {
- // We should call onBackCancelled() when an active callback is removed from dispatcher.
- sendCancelledIfInProgress(callback);
- setTopOnBackInvokedCallback(getTopCallback());
+ if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback) {
+ callback = mImeBackAnimationController;
+ }
+ if (!mAllCallbacks.containsKey(callback)) {
+ if (DEBUG) {
+ Log.i(TAG, "Callback not found. returning...");
+ }
+ return;
+ }
+ OnBackInvokedCallback previousTopCallback = getTopCallback();
+ Integer priority = mAllCallbacks.get(callback);
+ ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
+ callbacks.remove(callback);
+ if (callbacks.isEmpty()) {
+ mOnBackInvokedCallbacks.remove(priority);
+ }
+ mAllCallbacks.remove(callback);
+ // Re-populate the top callback to WM if the removed callback was previously the top
+ // one.
+ if (previousTopCallback == callback) {
+ // We should call onBackCancelled() when an active callback is removed from
+ // dispatcher.
+ sendCancelledIfInProgress(callback);
+ setTopOnBackInvokedCallback(getTopCallback());
+ }
}
}
@@ -191,15 +206,21 @@
* Indicates if the dispatcher is actively dispatching to a callback.
*/
public boolean isDispatching() {
- return mIsDispatching;
+ synchronized (mLock) {
+ return mIsDispatching;
+ }
}
private void onStartDispatching() {
- mIsDispatching = true;
+ synchronized (mLock) {
+ mIsDispatching = true;
+ }
}
private void onStopDispatching() {
- mIsDispatching = false;
+ synchronized (mLock) {
+ mIsDispatching = false;
+ }
}
private void sendCancelledIfInProgress(@NonNull OnBackInvokedCallback callback) {
@@ -223,27 +244,29 @@
/** Clears all registered callbacks on the instance. */
public void clear() {
- if (mImeDispatcher != null) {
- mImeDispatcher.clear();
- mImeDispatcher = null;
- }
- if (!mAllCallbacks.isEmpty()) {
- OnBackInvokedCallback topCallback = getTopCallback();
- if (topCallback != null) {
- sendCancelledIfInProgress(topCallback);
- } else {
- // Should not be possible
- Log.e(TAG, "There is no topCallback, even if mAllCallbacks is not empty");
+ synchronized (mLock) {
+ if (mImeDispatcher != null) {
+ mImeDispatcher.clear();
+ mImeDispatcher = null;
}
- // Clear binder references in WM.
- setTopOnBackInvokedCallback(null);
- }
+ if (!mAllCallbacks.isEmpty()) {
+ OnBackInvokedCallback topCallback = getTopCallback();
+ if (topCallback != null) {
+ sendCancelledIfInProgress(topCallback);
+ } else {
+ // Should not be possible
+ Log.e(TAG, "There is no topCallback, even if mAllCallbacks is not empty");
+ }
+ // Clear binder references in WM.
+ setTopOnBackInvokedCallback(null);
+ }
- // We should also stop running animations since all callbacks have been removed.
- // note: mSpring.skipToEnd(), in ProgressAnimator.reset(), requires the main handler.
- Handler.getMain().post(mProgressAnimator::reset);
- mAllCallbacks.clear();
- mOnBackInvokedCallbacks.clear();
+ // We should also stop running animations since all callbacks have been removed.
+ // note: mSpring.skipToEnd(), in ProgressAnimator.reset(), requires the main handler.
+ Handler.getMain().post(mProgressAnimator::reset);
+ mAllCallbacks.clear();
+ mOnBackInvokedCallbacks.clear();
+ }
}
private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallback callback) {
@@ -275,13 +298,15 @@
}
public OnBackInvokedCallback getTopCallback() {
- if (mAllCallbacks.isEmpty()) {
- return null;
- }
- for (Integer priority : mOnBackInvokedCallbacks.descendingKeySet()) {
- ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
- if (!callbacks.isEmpty()) {
- return callbacks.get(callbacks.size() - 1);
+ synchronized (mLock) {
+ if (mAllCallbacks.isEmpty()) {
+ return null;
+ }
+ for (Integer priority : mOnBackInvokedCallbacks.descendingKeySet()) {
+ ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
+ if (!callbacks.isEmpty()) {
+ return callbacks.get(callbacks.size() - 1);
+ }
}
}
return null;
@@ -315,16 +340,18 @@
public void dump(String prefix, PrintWriter writer) {
String innerPrefix = prefix + " ";
writer.println(prefix + "WindowOnBackDispatcher:");
- if (mAllCallbacks.isEmpty()) {
- writer.println(prefix + "<None>");
- return;
- }
+ synchronized (mLock) {
+ if (mAllCallbacks.isEmpty()) {
+ writer.println(prefix + "<None>");
+ return;
+ }
- writer.println(innerPrefix + "Top Callback: " + getTopCallback());
- writer.println(innerPrefix + "Callbacks: ");
- mAllCallbacks.forEach((callback, priority) -> {
- writer.println(innerPrefix + " Callback: " + callback + " Priority=" + priority);
- });
+ writer.println(innerPrefix + "Top Callback: " + getTopCallback());
+ writer.println(innerPrefix + "Callbacks: ");
+ mAllCallbacks.forEach((callback, priority) -> {
+ writer.println(innerPrefix + " Callback: " + callback + " Priority=" + priority);
+ });
+ }
}
static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 4a3aba1..1ffb4ff 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -25,6 +25,7 @@
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.ResourcesManager;
+import android.app.servertransaction.ClientTransactionListenerController;
import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -137,12 +138,33 @@
* should be dispatched to listeners.
*/
@AnyThread
- public void onConfigurationChanged(Configuration newConfig, int newDisplayId,
+ public void onConfigurationChanged(@NonNull Configuration newConfig, int newDisplayId,
boolean shouldReportConfigChange) {
final Context context = mContextRef.get();
if (context == null) {
return;
}
+ if (shouldReportConfigChange) {
+ // Only report to ClientTransactionListenerController when shouldReportConfigChange,
+ // which is on the MainThread.
+ final ClientTransactionListenerController controller =
+ getClientTransactionListenerController();
+ controller.onContextConfigurationPreChanged(context);
+ try {
+ onConfigurationChangedInner(context, newConfig, newDisplayId,
+ shouldReportConfigChange);
+ } finally {
+ controller.onContextConfigurationPostChanged(context);
+ }
+ } else {
+ onConfigurationChangedInner(context, newConfig, newDisplayId, shouldReportConfigChange);
+ }
+ }
+
+ /** Handles onConfiguration changed. */
+ @VisibleForTesting
+ public void onConfigurationChangedInner(@NonNull Context context,
+ @NonNull Configuration newConfig, int newDisplayId, boolean shouldReportConfigChange) {
CompatibilityInfo.applyOverrideScaleIfNeeded(newConfig);
final boolean displayChanged;
final boolean shouldUpdateResources;
@@ -220,4 +242,11 @@
mContextRef.clear();
}
}
+
+ /** Gets {@link ClientTransactionListenerController}. */
+ @VisibleForTesting
+ @NonNull
+ public ClientTransactionListenerController getClientTransactionListenerController() {
+ return ClientTransactionListenerController.getInstance();
+ }
}
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 9524a6e..65e5f1a 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -49,3 +49,10 @@
description: "Shows running apps in Desktop Mode Taskbar"
bug: "332504528"
}
+
+flag {
+ name: "enable_desktop_windowing_wallpaper_activity"
+ namespace: "lse_desktop_experience"
+ description: "Enables desktop wallpaper activity to show wallpaper in the desktop mode"
+ bug: "309014605"
+}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index ddb8ee0..06ae11fee 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -135,7 +135,7 @@
DialogStatus.SHOWN,
})
/** Denotes the user shortcut type. */
- @interface DialogStatus {
+ public @interface DialogStatus {
int NOT_SHOWN = 0;
int SHOWN = 1;
}
@@ -366,7 +366,7 @@
// to the Settings.
final ComponentName configDefaultService =
ComponentName.unflattenFromString(defaultService);
- if (Flags.a11yQsShortcut()) {
+ if (Flags.migrateEnableShortcuts()) {
am.enableShortcutsForTargets(true, HARDWARE,
Set.of(configDefaultService.flattenToString()), userId);
} else {
@@ -384,7 +384,7 @@
mContext,
HARDWARE,
userId);
- if (Flags.a11yQsShortcut()) {
+ if (Flags.migrateEnableShortcuts()) {
am.enableShortcutsForTargets(
false, HARDWARE, targetServices, userId);
} else {
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
index f088592..66faa31 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
@@ -117,7 +117,7 @@
@Override
public void onCheckedChanged(boolean isChecked) {
setShortcutEnabled(isChecked);
- if (Flags.a11yQsShortcut()) {
+ if (Flags.migrateEnableShortcuts()) {
final AccessibilityManager am =
getContext().getSystemService(AccessibilityManager.class);
am.enableShortcutsForTargets(
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 2a522645..8aba36b 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -582,23 +582,6 @@
* @param maxHistoryFiles the largest number of history buffer files to keep
* @param maxHistoryBufferSize the most amount of RAM to used for buffering of history steps
*/
- public BatteryStatsHistory(File systemDir, int maxHistoryFiles, int maxHistoryBufferSize,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
- MonotonicClock monotonicClock) {
- this(systemDir, maxHistoryFiles, maxHistoryBufferSize,
- stepDetailsCalculator, clock, monotonicClock, new TraceDelegate(),
- new EventLogger());
- }
-
- public BatteryStatsHistory(File systemDir, int maxHistoryFiles, int maxHistoryBufferSize,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
- MonotonicClock monotonicClock, TraceDelegate tracer, EventLogger eventLogger) {
- this(Parcel.obtain(), systemDir, maxHistoryFiles, maxHistoryBufferSize,
- stepDetailsCalculator, clock, monotonicClock, tracer, eventLogger);
- initHistoryBuffer();
- }
-
- @VisibleForTesting
public BatteryStatsHistory(Parcel historyBuffer, File systemDir,
int maxHistoryFiles, int maxHistoryBufferSize,
HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
@@ -607,12 +590,11 @@
clock, monotonicClock, tracer, eventLogger, null);
}
- private BatteryStatsHistory(Parcel historyBuffer, File systemDir,
+ private BatteryStatsHistory(@Nullable Parcel historyBuffer, @Nullable File systemDir,
int maxHistoryFiles, int maxHistoryBufferSize,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
- MonotonicClock monotonicClock, TraceDelegate tracer, EventLogger eventLogger,
- BatteryStatsHistory writableHistory) {
- mHistoryBuffer = historyBuffer;
+ @NonNull HistoryStepDetailsCalculator stepDetailsCalculator, @NonNull Clock clock,
+ @NonNull MonotonicClock monotonicClock, @NonNull TraceDelegate tracer,
+ @NonNull EventLogger eventLogger, @Nullable BatteryStatsHistory writableHistory) {
mSystemDir = systemDir;
mMaxHistoryBufferSize = maxHistoryBufferSize;
mStepDetailsCalculator = stepDetailsCalculator;
@@ -625,9 +607,16 @@
mMutable = false;
}
+ if (historyBuffer != null) {
+ mHistoryBuffer = historyBuffer;
+ } else {
+ mHistoryBuffer = Parcel.obtain();
+ initHistoryBuffer();
+ }
+
if (writableHistory != null) {
mHistoryDir = writableHistory.mHistoryDir;
- } else {
+ } else if (systemDir != null) {
mHistoryDir = new BatteryHistoryDirectory(new File(systemDir, HISTORY_DIR),
monotonicClock, maxHistoryFiles);
mHistoryDir.load();
@@ -636,35 +625,11 @@
activeFile = mHistoryDir.makeBatteryHistoryFile();
}
setActiveFile(activeFile);
+ } else {
+ mHistoryDir = null;
}
}
- public BatteryStatsHistory(int maxHistoryBufferSize,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
- MonotonicClock monotonicClock) {
- this(maxHistoryBufferSize, stepDetailsCalculator, clock, monotonicClock,
- new TraceDelegate(), new EventLogger());
- }
-
- @VisibleForTesting
- public BatteryStatsHistory(int maxHistoryBufferSize,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
- MonotonicClock monotonicClock, TraceDelegate traceDelegate,
- EventLogger eventLogger) {
- mMaxHistoryBufferSize = maxHistoryBufferSize;
- mStepDetailsCalculator = stepDetailsCalculator;
- mTracer = traceDelegate;
- mClock = clock;
- mMonotonicClock = monotonicClock;
- mEventLogger = eventLogger;
-
- mHistoryBuffer = Parcel.obtain();
- mSystemDir = null;
- mHistoryDir = null;
- mWritableHistory = null;
- initHistoryBuffer();
- }
-
/**
* Used when BatteryStatsHistory object is created from deserialization of a BatteryUsageStats
* parcel.
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index eef6ce7..07fa679 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -229,6 +229,17 @@
}
/**
+ * Copies time-in-state and timestamps from the supplied counter.
+ */
+ public void copyStatesFrom(LongArrayMultiStateCounter counter) {
+ if (mStateCount != counter.mStateCount) {
+ throw new IllegalArgumentException(
+ "State count is not the same: " + mStateCount + " vs. " + counter.mStateCount);
+ }
+ native_copyStatesFrom(mNativeObject, counter.mNativeObject);
+ }
+
+ /**
* Sets the new values for the given state.
*/
public void setValues(int state, long[] values) {
@@ -376,6 +387,10 @@
private static native void native_setState(long nativeObject, int state, long timestampMs);
@CriticalNative
+ private static native void native_copyStatesFrom(long nativeObjectTarget,
+ long nativeObjectSource);
+
+ @CriticalNative
private static native void native_setValues(long nativeObject, int state,
long longArrayContainerNativeObject);
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index ab982f5..9646ae9 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -18,6 +18,7 @@
import android.annotation.LongDef;
+import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.XmlRes;
import android.compat.annotation.UnsupportedAppUsage;
@@ -352,19 +353,39 @@
* WARNING: use only for testing!
*/
@VisibleForTesting
- public void forceInitForTesting(Context context, @XmlRes int xmlId) {
+ public void initForTesting(XmlPullParser parser) {
+ initForTesting(parser, null);
+ }
+
+ /**
+ * Reinitialize the PowerProfile with the provided XML, using optional Resources for fallback
+ * configuration settings.
+ * WARNING: use only for testing!
+ */
+ @VisibleForTesting
+ public void initForTesting(XmlPullParser parser, @Nullable Resources resources) {
synchronized (sLock) {
sPowerItemMap.clear();
sPowerArrayMap.clear();
sModemPowerProfile.clear();
- initLocked(context, xmlId);
+
+ try {
+ readPowerValuesFromXml(parser, resources);
+ } finally {
+ if (parser instanceof XmlResourceParser) {
+ ((XmlResourceParser) parser).close();
+ }
+ }
+ initLocked();
}
}
@GuardedBy("sLock")
private void initLocked(Context context, @XmlRes int xmlId) {
if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) {
- readPowerValuesFromXml(context, xmlId);
+ final Resources resources = context.getResources();
+ XmlResourceParser parser = resources.getXml(xmlId);
+ readPowerValuesFromXml(parser, resources);
}
initLocked();
}
@@ -377,9 +398,8 @@
initModem();
}
- private void readPowerValuesFromXml(Context context, @XmlRes int xmlId) {
- final Resources resources = context.getResources();
- XmlResourceParser parser = resources.getXml(xmlId);
+ private static void readPowerValuesFromXml(XmlPullParser parser,
+ @Nullable Resources resources) {
boolean parsingArray = false;
ArrayList<Double> array = new ArrayList<>();
String arrayName = null;
@@ -430,9 +450,17 @@
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
- parser.close();
+ if (parser instanceof XmlResourceParser) {
+ ((XmlResourceParser) parser).close();
+ }
}
+ if (resources != null) {
+ getDefaultValuesFromConfig(resources);
+ }
+ }
+
+ private static void getDefaultValuesFromConfig(Resources resources) {
// Now collect other config variables.
int[] configResIds = new int[]{
com.android.internal.R.integer.config_bluetooth_idle_cur_ma,
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 56263fb..7c7c7b8 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -47,7 +47,7 @@
private static final BatteryStatsHistory.VarintParceler VARINT_PARCELER =
new BatteryStatsHistory.VarintParceler();
- private static final byte PARCEL_FORMAT_VERSION = 1;
+ private static final byte PARCEL_FORMAT_VERSION = 2;
private static final int PARCEL_FORMAT_VERSION_MASK = 0x000000FF;
private static final int PARCEL_FORMAT_VERSION_SHIFT =
@@ -57,7 +57,12 @@
Integer.numberOfTrailingZeros(STATS_ARRAY_LENGTH_MASK);
public static final int MAX_STATS_ARRAY_LENGTH =
(1 << Integer.bitCount(STATS_ARRAY_LENGTH_MASK)) - 1;
- private static final int UID_STATS_ARRAY_LENGTH_MASK = 0x00FF0000;
+ private static final int STATE_STATS_ARRAY_LENGTH_MASK = 0x00FF0000;
+ private static final int STATE_STATS_ARRAY_LENGTH_SHIFT =
+ Integer.numberOfTrailingZeros(STATE_STATS_ARRAY_LENGTH_MASK);
+ public static final int MAX_STATE_STATS_ARRAY_LENGTH =
+ (1 << Integer.bitCount(STATE_STATS_ARRAY_LENGTH_MASK)) - 1;
+ private static final int UID_STATS_ARRAY_LENGTH_MASK = 0xFF000000;
private static final int UID_STATS_ARRAY_LENGTH_SHIFT =
Integer.numberOfTrailingZeros(UID_STATS_ARRAY_LENGTH_MASK);
public static final int MAX_UID_STATS_ARRAY_LENGTH =
@@ -74,6 +79,10 @@
private static final String XML_ATTR_ID = "id";
private static final String XML_ATTR_NAME = "name";
private static final String XML_ATTR_STATS_ARRAY_LENGTH = "stats-array-length";
+ private static final String XML_TAG_STATE = "state";
+ private static final String XML_ATTR_STATE_KEY = "key";
+ private static final String XML_ATTR_STATE_LABEL = "label";
+ private static final String XML_ATTR_STATE_STATS_ARRAY_LENGTH = "state-stats-array-length";
private static final String XML_ATTR_UID_STATS_ARRAY_LENGTH = "uid-stats-array-length";
private static final String XML_TAG_EXTRAS = "extras";
@@ -85,7 +94,24 @@
public final int powerComponentId;
public final String name;
+ /**
+ * Stats for the power component, such as the total usage time.
+ */
public final int statsArrayLength;
+
+ /**
+ * Map of device state codes to their corresponding human-readable labels.
+ */
+ public final SparseArray<String> stateLabels;
+
+ /**
+ * Stats for a specific state of the power component, e.g. "mobile radio in the 5G mode"
+ */
+ public final int stateStatsArrayLength;
+
+ /**
+ * Stats for the usage of this power component by a specific UID (app)
+ */
public final int uidStatsArrayLength;
/**
@@ -95,17 +121,25 @@
public final PersistableBundle extras;
public Descriptor(@BatteryConsumer.PowerComponent int powerComponentId,
- int statsArrayLength, int uidStatsArrayLength, @NonNull PersistableBundle extras) {
+ int statsArrayLength, @Nullable SparseArray<String> stateLabels,
+ int stateStatsArrayLength, int uidStatsArrayLength,
+ @NonNull PersistableBundle extras) {
this(powerComponentId, BatteryConsumer.powerComponentIdToString(powerComponentId),
- statsArrayLength, uidStatsArrayLength, extras);
+ statsArrayLength, stateLabels, stateStatsArrayLength, uidStatsArrayLength,
+ extras);
}
public Descriptor(int customPowerComponentId, String name, int statsArrayLength,
+ @Nullable SparseArray<String> stateLabels, int stateStatsArrayLength,
int uidStatsArrayLength, PersistableBundle extras) {
if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) {
throw new IllegalArgumentException(
"statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH);
}
+ if (stateStatsArrayLength > MAX_STATE_STATS_ARRAY_LENGTH) {
+ throw new IllegalArgumentException(
+ "stateStatsArrayLength is too high. Max = " + MAX_STATE_STATS_ARRAY_LENGTH);
+ }
if (uidStatsArrayLength > MAX_UID_STATS_ARRAY_LENGTH) {
throw new IllegalArgumentException(
"uidStatsArrayLength is too high. Max = " + MAX_UID_STATS_ARRAY_LENGTH);
@@ -113,11 +147,25 @@
this.powerComponentId = customPowerComponentId;
this.name = name;
this.statsArrayLength = statsArrayLength;
+ this.stateLabels = stateLabels != null ? stateLabels : new SparseArray<>();
+ this.stateStatsArrayLength = stateStatsArrayLength;
this.uidStatsArrayLength = uidStatsArrayLength;
this.extras = extras;
}
/**
+ * Returns the label associated with the give state key, e.g. "5G-high" for the
+ * state of Mobile Radio representing the 5G mode and high signal power.
+ */
+ public String getStateLabel(int key) {
+ String label = stateLabels.get(key);
+ if (label != null) {
+ return label;
+ }
+ return name + "-" + Integer.toHexString(key);
+ }
+
+ /**
* Writes the Descriptor into the parcel.
*/
public void writeSummaryToParcel(Parcel parcel) {
@@ -125,11 +173,18 @@
& PARCEL_FORMAT_VERSION_MASK)
| ((statsArrayLength << STATS_ARRAY_LENGTH_SHIFT)
& STATS_ARRAY_LENGTH_MASK)
+ | ((stateStatsArrayLength << STATE_STATS_ARRAY_LENGTH_SHIFT)
+ & STATE_STATS_ARRAY_LENGTH_MASK)
| ((uidStatsArrayLength << UID_STATS_ARRAY_LENGTH_SHIFT)
& UID_STATS_ARRAY_LENGTH_MASK);
parcel.writeInt(firstWord);
parcel.writeInt(powerComponentId);
parcel.writeString(name);
+ parcel.writeInt(stateLabels.size());
+ for (int i = 0, size = stateLabels.size(); i < size; i++) {
+ parcel.writeInt(stateLabels.keyAt(i));
+ parcel.writeString(stateLabels.valueAt(i));
+ }
extras.writeToParcel(parcel, 0);
}
@@ -148,13 +203,22 @@
}
int statsArrayLength =
(firstWord & STATS_ARRAY_LENGTH_MASK) >>> STATS_ARRAY_LENGTH_SHIFT;
+ int stateStatsArrayLength =
+ (firstWord & STATE_STATS_ARRAY_LENGTH_MASK) >>> STATE_STATS_ARRAY_LENGTH_SHIFT;
int uidStatsArrayLength =
(firstWord & UID_STATS_ARRAY_LENGTH_MASK) >>> UID_STATS_ARRAY_LENGTH_SHIFT;
int powerComponentId = parcel.readInt();
String name = parcel.readString();
+ int stateLabelCount = parcel.readInt();
+ SparseArray<String> stateLabels = new SparseArray<>(stateLabelCount);
+ for (int i = stateLabelCount; i > 0; i--) {
+ int key = parcel.readInt();
+ String label = parcel.readString();
+ stateLabels.put(key, label);
+ }
PersistableBundle extras = parcel.readPersistableBundle();
- return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength,
- extras);
+ return new Descriptor(powerComponentId, name, statsArrayLength, stateLabels,
+ stateStatsArrayLength, uidStatsArrayLength, extras);
}
@Override
@@ -163,11 +227,13 @@
if (!(o instanceof Descriptor)) return false;
Descriptor that = (Descriptor) o;
return powerComponentId == that.powerComponentId
- && statsArrayLength == that.statsArrayLength
- && uidStatsArrayLength == that.uidStatsArrayLength
- && Objects.equals(name, that.name)
- && extras.size() == that.extras.size() // Unparcel the Parcel if not yet
- && Bundle.kindofEquals(extras,
+ && statsArrayLength == that.statsArrayLength
+ && stateLabels.contentEquals(that.stateLabels)
+ && stateStatsArrayLength == that.stateStatsArrayLength
+ && uidStatsArrayLength == that.uidStatsArrayLength
+ && Objects.equals(name, that.name)
+ && extras.size() == that.extras.size() // Unparcel the Parcel if not yet
+ && Bundle.kindofEquals(extras,
that.extras); // Since the Parcel is now unparceled, do a deep comparison
}
@@ -179,7 +245,14 @@
serializer.attributeInt(null, XML_ATTR_ID, powerComponentId);
serializer.attribute(null, XML_ATTR_NAME, name);
serializer.attributeInt(null, XML_ATTR_STATS_ARRAY_LENGTH, statsArrayLength);
+ serializer.attributeInt(null, XML_ATTR_STATE_STATS_ARRAY_LENGTH, stateStatsArrayLength);
serializer.attributeInt(null, XML_ATTR_UID_STATS_ARRAY_LENGTH, uidStatsArrayLength);
+ for (int i = stateLabels.size() - 1; i >= 0; i--) {
+ serializer.startTag(null, XML_TAG_STATE);
+ serializer.attributeInt(null, XML_ATTR_STATE_KEY, stateLabels.keyAt(i));
+ serializer.attribute(null, XML_ATTR_STATE_LABEL, stateLabels.valueAt(i));
+ serializer.endTag(null, XML_TAG_STATE);
+ }
try {
serializer.startTag(null, XML_TAG_EXTRAS);
extras.saveToXml(serializer);
@@ -199,6 +272,8 @@
int powerComponentId = -1;
String name = null;
int statsArrayLength = 0;
+ SparseArray<String> stateLabels = new SparseArray<>();
+ int stateStatsArrayLength = 0;
int uidStatsArrayLength = 0;
PersistableBundle extras = null;
int eventType = parser.getEventType();
@@ -212,9 +287,16 @@
name = parser.getAttributeValue(null, XML_ATTR_NAME);
statsArrayLength = parser.getAttributeInt(null,
XML_ATTR_STATS_ARRAY_LENGTH);
+ stateStatsArrayLength = parser.getAttributeInt(null,
+ XML_ATTR_STATE_STATS_ARRAY_LENGTH);
uidStatsArrayLength = parser.getAttributeInt(null,
XML_ATTR_UID_STATS_ARRAY_LENGTH);
break;
+ case XML_TAG_STATE:
+ int value = parser.getAttributeInt(null, XML_ATTR_STATE_KEY);
+ String label = parser.getAttributeValue(null, XML_ATTR_STATE_LABEL);
+ stateLabels.put(value, label);
+ break;
case XML_TAG_EXTRAS:
extras = PersistableBundle.restoreFromXml(parser);
break;
@@ -225,11 +307,11 @@
if (powerComponentId == -1) {
return null;
} else if (powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
- return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength,
- extras);
+ return new Descriptor(powerComponentId, name, statsArrayLength,
+ stateLabels, stateStatsArrayLength, uidStatsArrayLength, extras);
} else if (powerComponentId < BatteryConsumer.POWER_COMPONENT_COUNT) {
- return new Descriptor(powerComponentId, statsArrayLength, uidStatsArrayLength,
- extras);
+ return new Descriptor(powerComponentId, statsArrayLength, stateLabels,
+ stateStatsArrayLength, uidStatsArrayLength, extras);
} else {
Slog.e(TAG, "Unrecognized power component: " + powerComponentId);
return null;
@@ -247,12 +329,14 @@
extras.size(); // Unparcel
}
return "PowerStats.Descriptor{"
- + "powerComponentId=" + powerComponentId
- + ", name='" + name + '\''
- + ", statsArrayLength=" + statsArrayLength
- + ", uidStatsArrayLength=" + uidStatsArrayLength
- + ", extras=" + extras
- + '}';
+ + "powerComponentId=" + powerComponentId
+ + ", name='" + name + '\''
+ + ", statsArrayLength=" + statsArrayLength
+ + ", stateStatsArrayLength=" + stateStatsArrayLength
+ + ", stateLabels=" + stateLabels
+ + ", uidStatsArrayLength=" + uidStatsArrayLength
+ + ", extras=" + extras
+ + '}';
}
}
@@ -293,6 +377,12 @@
public long[] stats;
/**
+ * Device-wide mode stats, used when the power component can operate in different modes,
+ * e.g. RATs such as LTE and 5G.
+ */
+ public final SparseArray<long[]> stateStats = new SparseArray<>();
+
+ /**
* Per-UID CPU stats.
*/
public final SparseArray<long[]> uidStats = new SparseArray<>();
@@ -313,6 +403,15 @@
parcel.writeInt(descriptor.powerComponentId);
parcel.writeLong(durationMs);
VARINT_PARCELER.writeLongArray(parcel, stats);
+
+ if (descriptor.stateStatsArrayLength != 0) {
+ parcel.writeInt(stateStats.size());
+ for (int i = 0; i < stateStats.size(); i++) {
+ parcel.writeInt(stateStats.keyAt(i));
+ VARINT_PARCELER.writeLongArray(parcel, stateStats.valueAt(i));
+ }
+ }
+
parcel.writeInt(uidStats.size());
for (int i = 0; i < uidStats.size(); i++) {
parcel.writeInt(uidStats.keyAt(i));
@@ -347,6 +446,17 @@
stats.durationMs = parcel.readLong();
stats.stats = new long[descriptor.statsArrayLength];
VARINT_PARCELER.readLongArray(parcel, stats.stats);
+
+ if (descriptor.stateStatsArrayLength != 0) {
+ int count = parcel.readInt();
+ for (int i = 0; i < count; i++) {
+ int state = parcel.readInt();
+ long[] stateStats = new long[descriptor.stateStatsArrayLength];
+ VARINT_PARCELER.readLongArray(parcel, stateStats);
+ stats.stateStats.put(state, stateStats);
+ }
+ }
+
int uidCount = parcel.readInt();
for (int i = 0; i < uidCount; i++) {
int uid = parcel.readInt();
@@ -376,6 +486,14 @@
if (stats.length > 0) {
sb.append("=").append(Arrays.toString(stats));
}
+ if (descriptor.stateStatsArrayLength != 0) {
+ for (int i = 0; i < stateStats.size(); i++) {
+ sb.append(" [");
+ sb.append(descriptor.getStateLabel(stateStats.keyAt(i)));
+ sb.append("]=");
+ sb.append(Arrays.toString(stateStats.valueAt(i)));
+ }
+ }
for (int i = 0; i < uidStats.size(); i++) {
sb.append(uidPrefix)
.append(UserHandle.formatUid(uidStats.keyAt(i)))
@@ -391,6 +509,18 @@
pw.println("PowerStats: " + descriptor.name + " (" + descriptor.powerComponentId + ')');
pw.increaseIndent();
pw.print("duration", durationMs).println();
+ if (descriptor.statsArrayLength != 0) {
+ pw.print("stats", Arrays.toString(stats)).println();
+ }
+ if (descriptor.stateStatsArrayLength != 0) {
+ for (int i = 0; i < stateStats.size(); i++) {
+ pw.print("state ");
+ pw.print(descriptor.getStateLabel(stateStats.keyAt(i)));
+ pw.print(": ");
+ pw.print(Arrays.toString(stateStats.valueAt(i)));
+ pw.println();
+ }
+ }
for (int i = 0; i < uidStats.size(); i++) {
pw.print("UID ");
pw.print(uidStats.keyAt(i));
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index e12becd..97ce96e 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -238,6 +238,7 @@
*/
public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
public static final int PARSE_APK_IN_APEX = 1 << 9;
+ public static final int PARSE_APEX = 1 << 10;
public static final int PARSE_CHATTY = 1 << 31;
@@ -339,6 +340,9 @@
if ((flags & PARSE_APK_IN_APEX) != 0) {
liteParseFlags |= PARSE_APK_IN_APEX;
}
+ if ((flags & PARSE_APEX) != 0) {
+ liteParseFlags |= PARSE_APEX;
+ }
final ParseResult<PackageLite> liteResult =
ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, liteParseFlags);
if (liteResult.isError()) {
@@ -530,7 +534,7 @@
afterParseBaseApplication(pkg);
- final ParseResult<ParsingPackage> result = validateBaseApkTags(input, pkg);
+ final ParseResult<ParsingPackage> result = validateBaseApkTags(input, pkg, flags);
if (result.isError()) {
return result;
}
@@ -1012,10 +1016,11 @@
}
}
- return validateBaseApkTags(input, pkg);
+ return validateBaseApkTags(input, pkg, flags);
}
- private ParseResult<ParsingPackage> validateBaseApkTags(ParseInput input, ParsingPackage pkg) {
+ private ParseResult<ParsingPackage> validateBaseApkTags(ParseInput input, ParsingPackage pkg,
+ int flags) {
if (!ParsedAttributionUtils.isCombinationValid(pkg.getAttributions())) {
return input.error(
INSTALL_PARSE_FAILED_BAD_MANIFEST,
@@ -1047,6 +1052,16 @@
adjustPackageToBeUnresizeableAndUnpipable(pkg);
}
+ // An Apex package shouldn't have permission declarations
+ final boolean isApex = (flags & PARSE_APEX) != 0;
+ if (isApex && !pkg.getPermissions().isEmpty()) {
+ return input.error(
+ INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ pkg.getPackageName()
+ + " is an APEX package and shouldn't declare permissions."
+ );
+ }
+
return input.success(pkg);
}
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 6848646..c6e8bf7 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -56,7 +56,6 @@
// during a configuration change.
private int mLastContentWidth;
private int mLastContentHeight;
- private int mLastCaptionHeight;
private int mLastXOffset;
private int mLastYOffset;
@@ -269,7 +268,7 @@
final boolean firstCall = mLastContentWidth == 0;
// The current content buffer is drawn here.
mLastContentWidth = xSize;
- mLastContentHeight = ySize - mLastCaptionHeight;
+ mLastContentHeight = ySize;
mLastXOffset = xOffset;
mLastYOffset = yOffset;
@@ -278,12 +277,11 @@
mLastXOffset,
mLastYOffset,
mLastXOffset + mLastContentWidth,
- mLastYOffset + mLastCaptionHeight + mLastContentHeight);
+ mLastYOffset + mLastContentHeight);
// If this was the first call and redrawLocked got already called prior
// to us, we should re-issue a redrawLocked now.
- return firstCall
- && (mLastCaptionHeight != 0 || !mDecorView.isShowingCaption());
+ return firstCall;
}
}
@@ -303,22 +301,9 @@
*/
private void redrawLocked(Rect newBounds, boolean fullscreen) {
- // While a configuration change is taking place the view hierarchy might become
- // inaccessible. For that case we remember the previous metrics to avoid flashes.
- // Note that even when there is no visible caption, the caption child will exist.
- final int captionHeight = mDecorView.getCaptionHeight();
-
- // The caption height will probably never dynamically change while we are resizing.
- // Once set to something other then 0 it should be kept that way.
- if (captionHeight != 0) {
- // Remember the height of the caption.
- mLastCaptionHeight = captionHeight;
- }
-
// Make sure that the other thread has already prepared the render draw calls for the
// content. If any size is 0, we have to wait for it to be drawn first.
- if ((mLastCaptionHeight == 0 && mDecorView.isShowingCaption()) ||
- mLastContentWidth == 0 || mLastContentHeight == 0) {
+ if (mLastContentWidth == 0 || mLastContentHeight == 0) {
return;
}
@@ -337,13 +322,13 @@
? mUserCaptionBackgroundDrawable : mCaptionBackgroundDrawable;
if (drawable != null) {
- drawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
+ drawable.setBounds(0, 0, left + width, top);
drawable.draw(canvas);
}
// The backdrop: clear everything with the background. Clipping is done elsewhere.
if (mResizingBackgroundDrawable != null) {
- mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
+ mResizingBackgroundDrawable.setBounds(0, 0, left + width, top + height);
mResizingBackgroundDrawable.draw(canvas);
}
mFrameAndBackdropNode.endRecording();
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index a65a1bb..ab37252 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -26,9 +26,6 @@
import static android.view.View.MeasureSpec.getMode;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
-import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
-import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
import static android.view.WindowInsetsController.APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -38,9 +35,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
@@ -83,7 +77,6 @@
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.PendingInsetsController;
-import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
@@ -116,7 +109,6 @@
import com.android.internal.view.menu.MenuHelper;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.BackgroundFallback;
-import com.android.internal.widget.DecorCaptionView;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import java.util.List;
@@ -189,8 +181,6 @@
private final Rect mFrameOffsets = new Rect();
- private boolean mHasCaption = false;
-
private boolean mChanging;
private Drawable mMenuBackground;
@@ -247,11 +237,6 @@
private Rect mTempRect;
- // This is the caption view for the window, containing the caption and window control
- // buttons. The visibility of this decor depends on the workspace and the window type.
- // If the window type does not require such a view, this member might be null.
- private DecorCaptionView mDecorCaptionView;
-
private boolean mWindowResizeCallbacksAdded = false;
private Drawable.Callback mLastBackgroundDrawableCb = null;
private BackdropFrameRenderer mBackdropFrameRenderer = null;
@@ -524,24 +509,6 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
- if (mHasCaption && isShowingCaption()) {
- // Don't dispatch ACTION_DOWN to the captionr if the window is resizable and the event
- // was (starting) outside the window. Window resizing events should be handled by
- // WindowManager.
- // TODO: Investigate how to handle the outside touch in window manager
- // without generating these events.
- // Currently we receive these because we need to enlarge the window's
- // touch region so that the monitor channel receives the events
- // in the outside touch area.
- if (action == MotionEvent.ACTION_DOWN) {
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- if (isOutOfInnerBounds(x, y)) {
- return true;
- }
- }
- }
-
if (mFeatureId >= 0) {
if (action == MotionEvent.ACTION_DOWN) {
int x = (int)event.getX();
@@ -1041,7 +1008,6 @@
@Override
public void onWindowSystemUiVisibilityChanged(int visible) {
updateColorViews(null /* insets */, true /* animate */);
- updateDecorCaptionStatus(getResources().getConfiguration());
if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
updateStatusGuardColor();
@@ -1214,11 +1180,6 @@
mLastTopInset, false /* matchVertical */, statusBarNeedsLeftInset,
statusBarSideInset, animate && !disallowAnimate,
mForceWindowDrawsBarBackgrounds, requestedVisibleTypes);
-
- if (mHasCaption) {
- mDecorCaptionView.getCaption().setBackgroundColor(statusBarColor);
- updateDecorCaptionShade();
- }
}
// When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or
@@ -1483,7 +1444,7 @@
mWindow.getAttributes().flags, force);
boolean show = state.attributes.isVisible(state.present, color,
mWindow.getAttributes().flags, force);
- boolean showView = show && !isResizing() && !mHasCaption && size > 0;
+ boolean showView = show && !isResizing() && size > 0;
boolean visibilityChanged = false;
View view = state.view;
@@ -1950,9 +1911,6 @@
@Override
public void onRootViewScrollYChanged(int rootScrollY) {
mRootScrollY = rootScrollY;
- if (mDecorCaptionView != null) {
- mDecorCaptionView.onRootViewScrollYChanged(rootScrollY);
- }
updateColorViewTranslations();
}
@@ -2141,31 +2099,6 @@
.addOnPreDrawListener(mFloatingToolbarPreDrawListener);
}
- /**
- * Informs the decor if the caption is attached and visible.
- * @param attachedAndVisible true when the decor is visible.
- * Note that this will even be called if there is no caption.
- **/
- void enableCaption(boolean attachedAndVisible) {
- if (mHasCaption != attachedAndVisible) {
- mHasCaption = attachedAndVisible;
- if (getForeground() != null) {
- drawableChanged();
- }
- notifyCaptionHeightChanged();
- }
- }
-
- /**
- * An interface to be called when the caption visibility or height changed, to report the
- * corresponding insets change to the InsetsController.
- */
- public void notifyCaptionHeightChanged() {
- if (!CAPTION_ON_SHELL) {
- getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight());
- }
- }
-
void setWindow(PhoneWindow phoneWindow) {
mWindow = phoneWindow;
Context context = getContext();
@@ -2191,8 +2124,6 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- updateDecorCaptionStatus(newConfig);
-
initializeElevation();
}
@@ -2214,29 +2145,6 @@
& View.SYSTEM_UI_FLAG_FULLSCREEN));
}
- private void updateDecorCaptionStatus(Configuration config) {
- final boolean displayWindowDecor = config.windowConfiguration.hasWindowDecorCaption()
- && !isFillingScreen(config);
- if (mDecorCaptionView == null && displayWindowDecor) {
- // Configuration now requires a caption.
- final LayoutInflater inflater = mWindow.getLayoutInflater();
- mDecorCaptionView = createDecorCaptionView(inflater);
- if (mDecorCaptionView != null) {
- if (mDecorCaptionView.getParent() == null) {
- addView(mDecorCaptionView, 0,
- new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- }
- removeView(mContentRoot);
- mDecorCaptionView.addView(mContentRoot,
- new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
- }
- } else if (mDecorCaptionView != null) {
- // We might have to change the kind of surface before we do anything else.
- mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
- enableCaption(displayWindowDecor);
- }
- }
-
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
if (mBackdropFrameRenderer != null) {
loadBackgroundDrawablesIfNeeded();
@@ -2246,20 +2154,10 @@
getCurrentColor(mNavigationColorViewState));
}
- mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);
- if (mDecorCaptionView != null) {
- if (mDecorCaptionView.getParent() == null) {
- addView(mDecorCaptionView,
- new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- }
- mDecorCaptionView.addView(root,
- new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
- } else {
- // Put it below the color views.
- addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- }
+ // Put it below the color views.
+ addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) root;
initializeElevation();
}
@@ -2285,89 +2183,6 @@
}
}
- // Free floating overlapping windows require a caption.
- private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
- DecorCaptionView decorCaptionView = null;
- for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
- View view = getChildAt(i);
- if (view instanceof DecorCaptionView) {
- // The decor was most likely saved from a relaunch - so reuse it.
- decorCaptionView = (DecorCaptionView) view;
- removeViewAt(i);
- }
- }
- final WindowManager.LayoutParams attrs = mWindow.getAttributes();
- final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
- attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION;
- final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
- // Only a non floating application window on one of the allowed workspaces can get a caption
- if (!mWindow.isFloating() && isApplication && winConfig.hasWindowDecorCaption()
- && !CAPTION_ON_SHELL) {
- // Dependent on the brightness of the used title we either use the
- // dark or the light button frame.
- if (decorCaptionView == null) {
- decorCaptionView = inflateDecorCaptionView(inflater);
- }
- decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
- } else {
- decorCaptionView = null;
- }
-
- // Tell the decor if it has a visible caption.
- enableCaption(decorCaptionView != null);
- return decorCaptionView;
- }
-
- private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
- final Context context = getContext();
- // We make a copy of the inflater, so it has the right context associated with it.
- inflater = inflater.from(context);
- final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
- null);
- setDecorCaptionShade(view);
- return view;
- }
-
- private void setDecorCaptionShade(DecorCaptionView view) {
- final int shade = mWindow.getDecorCaptionShade();
- switch (shade) {
- case DECOR_CAPTION_SHADE_LIGHT:
- setLightDecorCaptionShade(view);
- break;
- case DECOR_CAPTION_SHADE_DARK:
- setDarkDecorCaptionShade(view);
- break;
- default: {
- if ((getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0) {
- setDarkDecorCaptionShade(view);
- } else {
- setLightDecorCaptionShade(view);
- }
- break;
- }
- }
- }
-
- void updateDecorCaptionShade() {
- if (mDecorCaptionView != null) {
- setDecorCaptionShade(mDecorCaptionView);
- }
- }
-
- private void setLightDecorCaptionShade(DecorCaptionView view) {
- view.findViewById(R.id.maximize_window).setBackgroundResource(
- R.drawable.decor_maximize_button_light);
- view.findViewById(R.id.close_window).setBackgroundResource(
- R.drawable.decor_close_button_light);
- }
-
- private void setDarkDecorCaptionShade(DecorCaptionView view) {
- view.findViewById(R.id.maximize_window).setBackgroundResource(
- R.drawable.decor_maximize_button_dark);
- view.findViewById(R.id.close_window).setBackgroundResource(
- R.drawable.decor_close_button_dark);
- }
-
/**
* Returns the color used to fill areas the app has not rendered content to yet when the
* user is resizing the window of an activity in multi-window mode.
@@ -2405,17 +2220,11 @@
}
void clearContentView() {
- if (mDecorCaptionView != null) {
- mDecorCaptionView.removeContentView();
- } else {
- // This window doesn't have caption, so we need to remove everything except our views
- // we might have added.
- for (int i = getChildCount() - 1; i >= 0; i--) {
- View v = getChildAt(i);
- if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
- && v != mStatusGuard) {
- removeViewAt(i);
- }
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ View v = getChildAt(i);
+ if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
+ && v != mStatusGuard) {
+ removeViewAt(i);
}
}
}
@@ -2439,23 +2248,6 @@
if (mBackdropFrameRenderer != null) {
return;
}
- final ThreadedRenderer renderer = getThreadedRenderer();
- if (renderer != null && !CAPTION_ON_SHELL) {
- loadBackgroundDrawablesIfNeeded();
- WindowInsets rootInsets = getRootWindowInsets();
- mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
- initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
- mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
- getCurrentColor(mNavigationColorViewState), fullscreen,
- rootInsets.getInsets(WindowInsets.Type.systemBars()));
-
- // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
- // If we want to get the shadow shown while resizing, we would need to elevate a new
- // element which owns the caption and has the elevation.
- updateElevation();
-
- updateColorViews(null /* insets */, false);
- }
getViewRootImpl().requestInvalidateRootRenderNode();
}
@@ -2576,23 +2368,6 @@
}
}
- boolean isShowingCaption() {
- return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
- }
-
- int getCaptionHeight() {
- return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
- }
-
- /**
- * @hide
- * @return the height of insets covering the top of window content area.
- */
- public int getCaptionInsetsHeight() {
- if (!mWindow.isOverlayWithDecorCaptionEnabled()) return 0;
- return getCaptionHeight();
- }
-
/**
* Converts a DIP measure into physical pixels.
* @param dip The dip value.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index e6a2a6c..1841353 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -363,8 +363,6 @@
private boolean mIsStartingWindow;
private int mTheme = -1;
- private int mDecorCaptionShade = DECOR_CAPTION_SHADE_AUTO;
-
private boolean mUseDecorContext = false;
/** @see ViewRootImpl#mActivityConfigCallback */
@@ -4048,14 +4046,7 @@
@Override
public void setDecorCaptionShade(int decorCaptionShade) {
- mDecorCaptionShade = decorCaptionShade;
- if (mDecor != null) {
- mDecor.updateDecorCaptionShade();
- }
- }
-
- int getDecorCaptionShade() {
- return mDecorCaptionShade;
+ // TODO(b/328668781): Make proper treatment to this public API per the outcome of the bug.
}
@Override
diff --git a/core/java/com/android/internal/power/ModemPowerProfile.java b/core/java/com/android/internal/power/ModemPowerProfile.java
index b15c10e..64d3139 100644
--- a/core/java/com/android/internal/power/ModemPowerProfile.java
+++ b/core/java/com/android/internal/power/ModemPowerProfile.java
@@ -17,14 +17,16 @@
package com.android.internal.power;
import android.annotation.IntDef;
-import android.content.res.XmlResourceParser;
+import android.os.BatteryStats;
import android.telephony.ModemActivityInfo;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseDoubleArray;
+import com.android.internal.os.PowerProfile;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -95,6 +97,8 @@
*/
public static final int MODEM_DRAIN_TYPE_TX = 0x3000_0000;
+ private static final int IGNORE = -1;
+
@IntDef(prefix = {"MODEM_DRAIN_TYPE_"}, value = {
MODEM_DRAIN_TYPE_SLEEP,
MODEM_DRAIN_TYPE_IDLE,
@@ -256,7 +260,7 @@
/**
* Generates a ModemPowerProfile object from the <modem /> element of a power_profile.xml
*/
- public void parseFromXml(XmlResourceParser parser) throws IOException,
+ public void parseFromXml(XmlPullParser parser) throws IOException,
XmlPullParserException {
final int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
@@ -286,7 +290,7 @@
}
/** Parse the <active /> XML element */
- private void parseActivePowerConstantsFromXml(XmlResourceParser parser)
+ private void parseActivePowerConstantsFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
// Parse attributes to get the type of active modem usage the power constants are for.
final int ratType;
@@ -339,7 +343,7 @@
}
}
- private static int getTypeFromAttribute(XmlResourceParser parser, String attr,
+ private static int getTypeFromAttribute(XmlPullParser parser, String attr,
SparseArray<String> names) {
final String value = XmlUtils.readStringAttribute(parser, attr);
if (value == null) {
@@ -382,6 +386,84 @@
}
}
+ public static long getAverageBatteryDrainKey(@ModemDrainType int drainType,
+ @BatteryStats.RadioAccessTechnology int rat, @ServiceState.FrequencyRange int freqRange,
+ int txLevel) {
+ long key = PowerProfile.SUBSYSTEM_MODEM;
+
+ // Attach Modem drain type to the key if specified.
+ if (drainType != IGNORE) {
+ key |= drainType;
+ }
+
+ // Attach RadioAccessTechnology to the key if specified.
+ switch (rat) {
+ case IGNORE:
+ // do nothing
+ break;
+ case BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER:
+ key |= MODEM_RAT_TYPE_DEFAULT;
+ break;
+ case BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE:
+ key |= MODEM_RAT_TYPE_LTE;
+ break;
+ case BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR:
+ key |= MODEM_RAT_TYPE_NR;
+ break;
+ default:
+ Log.w(TAG, "Unexpected RadioAccessTechnology : " + rat);
+ }
+
+ // Attach NR Frequency Range to the key if specified.
+ switch (freqRange) {
+ case IGNORE:
+ // do nothing
+ break;
+ case ServiceState.FREQUENCY_RANGE_UNKNOWN:
+ key |= MODEM_NR_FREQUENCY_RANGE_DEFAULT;
+ break;
+ case ServiceState.FREQUENCY_RANGE_LOW:
+ key |= MODEM_NR_FREQUENCY_RANGE_LOW;
+ break;
+ case ServiceState.FREQUENCY_RANGE_MID:
+ key |= MODEM_NR_FREQUENCY_RANGE_MID;
+ break;
+ case ServiceState.FREQUENCY_RANGE_HIGH:
+ key |= MODEM_NR_FREQUENCY_RANGE_HIGH;
+ break;
+ case ServiceState.FREQUENCY_RANGE_MMWAVE:
+ key |= MODEM_NR_FREQUENCY_RANGE_MMWAVE;
+ break;
+ default:
+ Log.w(TAG, "Unexpected NR frequency range : " + freqRange);
+ }
+
+ // Attach transmission level to the key if specified.
+ switch (txLevel) {
+ case IGNORE:
+ // do nothing
+ break;
+ case 0:
+ key |= MODEM_TX_LEVEL_0;
+ break;
+ case 1:
+ key |= MODEM_TX_LEVEL_1;
+ break;
+ case 2:
+ key |= MODEM_TX_LEVEL_2;
+ break;
+ case 3:
+ key |= MODEM_TX_LEVEL_3;
+ break;
+ case 4:
+ key |= MODEM_TX_LEVEL_4;
+ break;
+ default:
+ Log.w(TAG, "Unexpected transmission level : " + txLevel);
+ }
+ return key;
+ }
+
/**
* Returns the average battery drain in milli-amps of the modem for a given drain type.
* Returns {@link Double.NaN} if a suitable value is not found for the given key.
@@ -444,6 +526,7 @@
}
return sb.toString();
}
+
private static void appendFieldToString(StringBuilder sb, String fieldName,
SparseArray<String> names, int key) {
sb.append(fieldName);
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index fca4e91..b6558cb 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -440,6 +440,7 @@
}
private int internStacktraceString(TracingContext<
+ ProtoLogDataSource.Instance,
ProtoLogDataSource.TlsState,
ProtoLogDataSource.IncrementalState> ctx,
String stacktrace) {
@@ -449,7 +450,8 @@
}
private int internStringArg(
- TracingContext<ProtoLogDataSource.TlsState, ProtoLogDataSource.IncrementalState> ctx,
+ TracingContext<ProtoLogDataSource.Instance, ProtoLogDataSource.TlsState,
+ ProtoLogDataSource.IncrementalState> ctx,
String string
) {
final ProtoLogDataSource.IncrementalState incrementalState = ctx.getIncrementalState();
@@ -458,7 +460,8 @@
}
private int internString(
- TracingContext<ProtoLogDataSource.TlsState, ProtoLogDataSource.IncrementalState> ctx,
+ TracingContext<ProtoLogDataSource.Instance, ProtoLogDataSource.TlsState,
+ ProtoLogDataSource.IncrementalState> ctx,
Map<String, Integer> internMap,
long fieldId,
String string
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f5b1a47..f931a76 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -28,6 +28,7 @@
import android.media.MediaRoute2Info;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
import android.view.KeyEvent;
import android.service.notification.StatusBarNotification;
@@ -384,9 +385,11 @@
/**
* Shows the media output switcher dialog.
*
- * @param packageName of the session for which the output switcher is shown.
+ * @param targetPackageName The package name for which to show the output switcher.
+ * @param targetUserHandle The UserHandle on which the package for which to show the output
+ * switcher is running.
*/
- void showMediaOutputSwitcher(String packageName);
+ void showMediaOutputSwitcher(String targetPackageName, in UserHandle targetUserHandle);
/** Enters desktop mode from the current focused app.
*
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 9c63d0d..bd654fa 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -1212,6 +1212,10 @@
}
}
}
+ if (android.app.Flags.cleanUpSpansAndNewLines() && conversationText != null) {
+ // remove formatting from title.
+ conversationText = conversationText.toString();
+ }
if (conversationIcon == null) {
conversationIcon = largeIcon;
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
deleted file mode 100644
index 362fd7b..0000000
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.Window;
-
-import com.android.internal.R;
-import com.android.internal.policy.DecorView;
-import com.android.internal.policy.PhoneWindow;
-
-import java.util.ArrayList;
-
-/**
- * This class represents the special screen elements to control a window on freeform
- * environment.
- * As such this class handles the following things:
- * <ul>
- * <li>The caption, containing the system buttons like maximize, close and such as well as
- * allowing the user to drag the window around.</li>
- * </ul>
- * After creating the view, the function {@link #setPhoneWindow} needs to be called to make
- * the connection to it's owning PhoneWindow.
- * Note: At this time the application can change various attributes of the DecorView which
- * will break things (in subtle/unexpected ways):
- * <ul>
- * <li>setOutlineProvider</li>
- * <li>setSurfaceFormat</li>
- * <li>..</li>
- * </ul>
- *
- * Here describe the behavior of overlaying caption on the content and drawing.
- *
- * First, no matter where the content View gets added, it will always be the first child and the
- * caption will be the second. This way the caption will always be drawn on top of the content when
- * overlaying is enabled.
- *
- * Second, the touch dispatch is customized to handle overlaying. This is what happens when touch
- * is dispatched on the caption area while overlaying it on content:
- * <ul>
- * <li>DecorCaptionView.onInterceptTouchEvent() will try intercepting the touch events if the
- * down action is performed on top close or maximize buttons; the reason for that is we want these
- * buttons to always work.</li>
- * <li>The caption view will try to consume the event to apply the dragging logic.</li>
- * <li>If the touch event is not consumed by the caption, the content View will receive the touch
- * event</li>
- * </ul>
- */
-public class DecorCaptionView extends ViewGroup implements View.OnTouchListener,
- GestureDetector.OnGestureListener {
- private PhoneWindow mOwner = null;
- private boolean mShow = false;
-
- // True if the window is being dragged.
- private boolean mDragging = false;
-
- private boolean mOverlayWithAppContent = false;
-
- private View mCaption;
- private View mContent;
- private View mMaximize;
- private View mClose;
-
- // Fields for detecting drag events.
- private int mTouchDownX;
- private int mTouchDownY;
- private boolean mCheckForDragging;
- private int mDragSlop;
-
- // Fields for detecting and intercepting click events on close/maximize.
- private ArrayList<View> mTouchDispatchList = new ArrayList<>(2);
- // We use the gesture detector to detect clicks on close/maximize buttons and to be consistent
- // with existing click detection.
- private GestureDetector mGestureDetector;
- private final Rect mCloseRect = new Rect();
- private final Rect mMaximizeRect = new Rect();
- private View mClickTarget;
- private int mRootScrollY;
-
- public DecorCaptionView(Context context) {
- super(context);
- init(context);
- }
-
- public DecorCaptionView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context);
- }
-
- public DecorCaptionView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init(context);
- }
-
- private void init(Context context) {
- mDragSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- mGestureDetector = new GestureDetector(context, this);
- setContentDescription(context.getString(R.string.accessibility_freeform_caption,
- context.getPackageManager().getApplicationLabel(context.getApplicationInfo())));
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mCaption = getChildAt(0);
- }
-
- public void setPhoneWindow(PhoneWindow owner, boolean show) {
- mOwner = owner;
- mShow = show;
- mOverlayWithAppContent = owner.isOverlayWithDecorCaptionEnabled();
- updateCaptionVisibility();
- // By changing the outline provider to BOUNDS, the window can remove its
- // background without removing the shadow.
- mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS);
- mMaximize = findViewById(R.id.maximize_window);
- mClose = findViewById(R.id.close_window);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- // If the user starts touch on the maximize/close buttons, we immediately intercept, so
- // that these buttons are always clickable.
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- final int x = (int) ev.getX();
- final int y = (int) ev.getY();
- // Only offset y for containment tests because the actual views are already translated.
- if (mMaximizeRect.contains(x, y - mRootScrollY)) {
- mClickTarget = mMaximize;
- }
- if (mCloseRect.contains(x, y - mRootScrollY)) {
- mClickTarget = mClose;
- }
- }
- return mClickTarget != null;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mClickTarget != null) {
- mGestureDetector.onTouchEvent(event);
- final int action = event.getAction();
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- mClickTarget = null;
- }
- return true;
- }
- return false;
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent e) {
- // Note: There are no mixed events. When a new device gets used (e.g. 1. Mouse, 2. touch)
- // the old input device events get cancelled first. So no need to remember the kind of
- // input device we are listening to.
- final int x = (int) e.getX();
- final int y = (int) e.getY();
- final boolean fromMouse = e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_MOUSE;
- final boolean primaryButton = (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0;
- final int actionMasked = e.getActionMasked();
- switch (actionMasked) {
- case MotionEvent.ACTION_DOWN:
- if (!mShow) {
- // When there is no caption we should not react to anything.
- return false;
- }
- // Checking for a drag action is started if we aren't dragging already and the
- // starting event is either a left mouse button or any other input device.
- if (!fromMouse || primaryButton) {
- mCheckForDragging = true;
- mTouchDownX = x;
- mTouchDownY = y;
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (!mDragging && mCheckForDragging && (fromMouse || passedSlop(x, y))) {
- mCheckForDragging = false;
- mDragging = true;
- startMovingTask(e.getRawX(), e.getRawY());
- // After the above call the framework will take over the input.
- // This handler will receive ACTION_CANCEL soon (possible after a few spurious
- // ACTION_MOVE events which are safe to ignore).
- }
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (!mDragging) {
- break;
- }
- // Abort the ongoing dragging.
- if (actionMasked == MotionEvent.ACTION_UP) {
- // If it receives ACTION_UP event, the dragging is already finished and also
- // the system can not end drag on ACTION_UP event. So request to finish
- // dragging.
- finishMovingTask();
- }
- mDragging = false;
- return !mCheckForDragging;
- }
- return mDragging || mCheckForDragging;
- }
-
- @Override
- public boolean shouldDelayChildPressedState() {
- return false;
- }
-
- private boolean passedSlop(int x, int y) {
- return Math.abs(x - mTouchDownX) > mDragSlop || Math.abs(y - mTouchDownY) > mDragSlop;
- }
-
- /**
- * The phone window configuration has changed and the caption needs to be updated.
- * @param show True if the caption should be shown.
- */
- public void onConfigurationChanged(boolean show) {
- mShow = show;
- updateCaptionVisibility();
- }
-
- @Override
- public void addView(View child, int index, ViewGroup.LayoutParams params) {
- if (!(params instanceof MarginLayoutParams)) {
- throw new IllegalArgumentException(
- "params " + params + " must subclass MarginLayoutParams");
- }
- // Make sure that we never get more then one client area in our view.
- if (index >= 2 || getChildCount() >= 2) {
- throw new IllegalStateException("DecorCaptionView can only handle 1 client view");
- }
- // To support the overlaying content in the caption, we need to put the content view as the
- // first child to get the right Z-Ordering.
- super.addView(child, 0, params);
- mContent = child;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int captionHeight;
- if (mCaption.getVisibility() != View.GONE) {
- measureChildWithMargins(mCaption, widthMeasureSpec, 0, heightMeasureSpec, 0);
- captionHeight = mCaption.getMeasuredHeight();
- } else {
- captionHeight = 0;
- }
- if (mContent != null) {
- if (mOverlayWithAppContent) {
- measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0);
- } else {
- measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec,
- captionHeight);
- }
- }
-
- setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
- MeasureSpec.getSize(heightMeasureSpec));
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- final int captionHeight;
- if (mCaption.getVisibility() != View.GONE) {
- mCaption.layout(0, 0, mCaption.getMeasuredWidth(), mCaption.getMeasuredHeight());
- captionHeight = mCaption.getBottom() - mCaption.getTop();
- mMaximize.getHitRect(mMaximizeRect);
- mClose.getHitRect(mCloseRect);
- } else {
- captionHeight = 0;
- mMaximizeRect.setEmpty();
- mCloseRect.setEmpty();
- }
-
- if (mContent != null) {
- if (mOverlayWithAppContent) {
- mContent.layout(0, 0, mContent.getMeasuredWidth(), mContent.getMeasuredHeight());
- } else {
- mContent.layout(0, captionHeight, mContent.getMeasuredWidth(),
- captionHeight + mContent.getMeasuredHeight());
- }
- }
-
- ((DecorView) mOwner.getDecorView()).notifyCaptionHeightChanged();
-
- // This assumes that the caption bar is at the top.
- mOwner.notifyRestrictedCaptionAreaCallback(mMaximize.getLeft(), mMaximize.getTop(),
- mClose.getRight(), mClose.getBottom());
- }
-
- /**
- * Updates the visibility of the caption.
- **/
- private void updateCaptionVisibility() {
- mCaption.setVisibility(mShow ? VISIBLE : GONE);
- mCaption.setOnTouchListener(this);
- }
-
- /**
- * Maximize or restore the window by moving it to the maximized or freeform workspace stack.
- **/
- private void toggleFreeformWindowingMode() {
- Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
- if (callback != null) {
- callback.toggleFreeformWindowingMode();
- }
- }
-
- public boolean isCaptionShowing() {
- return mShow;
- }
-
- public int getCaptionHeight() {
- return (mCaption != null) ? mCaption.getHeight() : 0;
- }
-
- public void removeContentView() {
- if (mContent != null) {
- removeView(mContent);
- mContent = null;
- }
- }
-
- public View getCaption() {
- return mCaption;
- }
-
- @Override
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new MarginLayoutParams(getContext(), attrs);
- }
-
- @Override
- protected LayoutParams generateDefaultLayoutParams() {
- return new MarginLayoutParams(MarginLayoutParams.MATCH_PARENT,
- MarginLayoutParams.MATCH_PARENT);
- }
-
- @Override
- protected LayoutParams generateLayoutParams(LayoutParams p) {
- return new MarginLayoutParams(p);
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof MarginLayoutParams;
- }
-
- @Override
- public boolean onDown(MotionEvent e) {
- return false;
- }
-
- @Override
- public void onShowPress(MotionEvent e) {
-
- }
-
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- if (mClickTarget == mMaximize) {
- toggleFreeformWindowingMode();
- } else if (mClickTarget == mClose) {
- mOwner.dispatchOnWindowDismissed(
- true /*finishTask*/, false /*suppressWindowTransition*/);
- }
- return true;
- }
-
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- return false;
- }
-
- @Override
- public void onLongPress(MotionEvent e) {
-
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- return false;
- }
-
- /**
- * Called when {@link android.view.ViewRootImpl} scrolls for adjustPan.
- */
- public void onRootViewScrollYChanged(int scrollY) {
- // Offset the caption opposite the root scroll. This keeps the caption at the
- // top of the window during adjustPan.
- if (mCaption != null) {
- mRootScrollY = scrollY;
- mCaption.setTranslationY(scrollY);
- }
- }
-}
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index f8f1049..97a2d3b 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
+import android.app.Flags;
import android.app.Person;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -200,6 +201,10 @@
if (nameOverride == null) {
nameOverride = sender.getName();
}
+ if (Flags.cleanUpSpansAndNewLines() && nameOverride != null) {
+ // remove formatting from sender name
+ nameOverride = nameOverride.toString();
+ }
mSenderName = nameOverride;
if (mSingleLine && !TextUtils.isEmpty(nameOverride)) {
nameOverride = mContext.getResources().getString(
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 03b57d0..773823d 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -21,6 +21,7 @@
config_namespace: "ANDROID",
bool_variables: [
"release_binder_death_recipient_weak_from_jni",
+ "release_package_libandroid_runtime_punch_holes",
],
properties: [
"cflags",
@@ -63,6 +64,9 @@
release_binder_death_recipient_weak_from_jni: {
cflags: ["-DBINDER_DEATH_RECIPIENT_WEAK_FROM_JNI"],
},
+ release_package_libandroid_runtime_punch_holes: {
+ cflags: ["-DENABLE_PUNCH_HOLES"],
+ },
},
cpp_std: "gnu++20",
@@ -123,6 +127,7 @@
srcs: [
"AndroidRuntime.cpp",
"com_android_internal_content_F2fsUtils.cpp",
+ "com_android_internal_content_FileSystemUtils.cpp",
"com_android_internal_content_NativeLibraryHelper.cpp",
"com_google_android_gles_jni_EGLImpl.cpp",
"com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm
diff --git a/core/jni/android_tracing_PerfettoDataSource.cpp b/core/jni/android_tracing_PerfettoDataSource.cpp
index 5c7b470..f82ebfe 100644
--- a/core/jni/android_tracing_PerfettoDataSource.cpp
+++ b/core/jni/android_tracing_PerfettoDataSource.cpp
@@ -93,49 +93,6 @@
return instance;
}
-jobject PerfettoDataSource::createTlsStateGlobalRef(JNIEnv* env, PerfettoDsInstanceIndex inst_id) {
- ScopedLocalRef<jobject> args(env,
- env->NewObject(gCreateTlsStateArgsClassInfo.clazz,
- gCreateTlsStateArgsClassInfo.init, mJavaDataSource,
- inst_id));
-
- ScopedLocalRef<jobject> tslState(env,
- env->CallObjectMethod(mJavaDataSource,
- gPerfettoDataSourceClassInfo
- .createTlsState,
- args.get()));
-
- if (env->ExceptionCheck()) {
- LOGE_EX(env);
- env->ExceptionClear();
- LOG_ALWAYS_FATAL("Failed to create new Java Perfetto incremental state");
- }
-
- return env->NewGlobalRef(tslState.get());
-}
-
-jobject PerfettoDataSource::createIncrementalStateGlobalRef(JNIEnv* env,
- PerfettoDsInstanceIndex inst_id) {
- ScopedLocalRef<jobject> args(env,
- env->NewObject(gCreateIncrementalStateArgsClassInfo.clazz,
- gCreateIncrementalStateArgsClassInfo.init,
- mJavaDataSource, inst_id));
-
- ScopedLocalRef<jobject> incrementalState(env,
- env->CallObjectMethod(mJavaDataSource,
- gPerfettoDataSourceClassInfo
- .createIncrementalState,
- args.get()));
-
- if (env->ExceptionCheck()) {
- LOGE_EX(env);
- env->ExceptionClear();
- LOG_ALWAYS_FATAL("Failed to create Java Perfetto incremental state");
- }
-
- return env->NewGlobalRef(incrementalState.get());
-}
-
bool PerfettoDataSource::TraceIterateBegin() {
if (gInIteration) {
return false;
@@ -177,6 +134,15 @@
gInIteration = false;
}
+PerfettoDsInstanceIndex PerfettoDataSource::GetInstanceIndex() {
+ if (!gInIteration) {
+ LOG_ALWAYS_FATAL("Tried calling GetInstanceIndex outside of a tracer iteration.");
+ return -1;
+ }
+
+ return gIterator.impl.inst_id;
+}
+
jobject PerfettoDataSource::GetCustomTls() {
if (!gInIteration) {
LOG_ALWAYS_FATAL("Tried getting CustomTls outside of a tracer iteration.");
@@ -189,6 +155,18 @@
return tls_state->jobj;
}
+void PerfettoDataSource::SetCustomTls(jobject tlsState) {
+ if (!gInIteration) {
+ LOG_ALWAYS_FATAL("Tried getting CustomTls outside of a tracer iteration.");
+ return;
+ }
+
+ TlsState* tls_state =
+ reinterpret_cast<TlsState*>(PerfettoDsGetCustomTls(&dataSource, &gIterator));
+
+ tls_state->jobj = tlsState;
+}
+
jobject PerfettoDataSource::GetIncrementalState() {
if (!gInIteration) {
LOG_ALWAYS_FATAL("Tried getting IncrementalState outside of a tracer iteration.");
@@ -201,6 +179,18 @@
return incr_state->jobj;
}
+void PerfettoDataSource::SetIncrementalState(jobject incrementalState) {
+ if (!gInIteration) {
+ LOG_ALWAYS_FATAL("Tried getting IncrementalState outside of a tracer iteration.");
+ return;
+ }
+
+ IncrementalState* incr_state = reinterpret_cast<IncrementalState*>(
+ PerfettoDsGetIncrementalState(&dataSource, &gIterator));
+
+ incr_state->jobj = incrementalState;
+}
+
void PerfettoDataSource::WritePackets(JNIEnv* env, jobjectArray packets) {
if (!gInIteration) {
LOG_ALWAYS_FATAL("Tried writing packets outside of a tracer iteration.");
@@ -211,7 +201,7 @@
for (int i = 0; i < packets_count; i++) {
jbyteArray packet_proto_buffer = (jbyteArray)env->GetObjectArrayElement(packets, i);
- jbyte* raw_proto_buffer = env->GetByteArrayElements(packet_proto_buffer, 0);
+ jbyte* raw_proto_buffer = env->GetByteArrayElements(packet_proto_buffer, nullptr);
int buffer_size = env->GetArrayLength(packet_proto_buffer);
struct PerfettoDsRootTracePacket trace_packet;
@@ -219,6 +209,8 @@
PerfettoPbMsgAppendBytes(&trace_packet.msg.msg, (const uint8_t*)raw_proto_buffer,
buffer_size);
PerfettoDsTracerPacketEnd(&gIterator, &trace_packet);
+
+ env->ReleaseByteArrayElements(packet_proto_buffer, raw_proto_buffer, 0 /* default mode */);
}
}
@@ -264,7 +256,7 @@
}
void nativeRegisterDataSource(JNIEnv* env, jclass clazz, jlong datasource_ptr,
- int buffer_exhausted_policy) {
+ jint buffer_exhausted_policy) {
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(datasource_ptr);
struct PerfettoDsParams params = PerfettoDsParamsDefault();
@@ -291,13 +283,8 @@
params.on_create_tls_cb = [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id,
struct PerfettoDsTracerImpl* tracer, void* user_arg) -> void* {
- JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
-
- auto* datasource = reinterpret_cast<PerfettoDataSource*>(user_arg);
-
- jobject java_tls_state = datasource->createTlsStateGlobalRef(env, inst_id);
-
- auto* tls_state = new TlsState(java_tls_state);
+ // Populated later and only if required by the java side
+ auto* tls_state = new TlsState(NULL);
return static_cast<void*>(tls_state);
};
@@ -306,18 +293,16 @@
TlsState* tls_state = reinterpret_cast<TlsState*>(ptr);
- env->DeleteGlobalRef(tls_state->jobj);
+ if (tls_state->jobj != NULL) {
+ env->DeleteGlobalRef(tls_state->jobj);
+ }
delete tls_state;
};
params.on_create_incr_cb = [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id,
struct PerfettoDsTracerImpl* tracer, void* user_arg) -> void* {
- JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
-
- auto* datasource = reinterpret_cast<PerfettoDataSource*>(user_arg);
- jobject java_incr_state = datasource->createIncrementalStateGlobalRef(env, inst_id);
-
- auto* incr_state = new IncrementalState(java_incr_state);
+ // Populated later and only if required by the java side
+ auto* incr_state = new IncrementalState(NULL);
return static_cast<void*>(incr_state);
};
@@ -326,7 +311,9 @@
IncrementalState* incr_state = reinterpret_cast<IncrementalState*>(ptr);
- env->DeleteGlobalRef(incr_state->jobj);
+ if (incr_state->jobj != NULL) {
+ env->DeleteGlobalRef(incr_state->jobj);
+ }
delete incr_state;
};
@@ -386,31 +373,49 @@
PerfettoDsImplReleaseInstanceLocked(datasource->dataSource.impl, instance_idx);
}
-bool nativePerfettoDsTraceIterateBegin(jlong dataSourcePtr) {
+bool nativePerfettoDsTraceIterateBegin(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) {
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
return datasource->TraceIterateBegin();
}
-bool nativePerfettoDsTraceIterateNext(jlong dataSourcePtr) {
+bool nativePerfettoDsTraceIterateNext(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) {
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
return datasource->TraceIterateNext();
}
-void nativePerfettoDsTraceIterateBreak(jlong dataSourcePtr) {
+void nativePerfettoDsTraceIterateBreak(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) {
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
return datasource->TraceIterateBreak();
}
+jint nativeGetPerfettoDsInstanceIndex(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) {
+ sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
+ return (jint)datasource->GetInstanceIndex();
+}
+
jobject nativeGetCustomTls(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) {
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
return datasource->GetCustomTls();
}
+void nativeSetCustomTls(JNIEnv* env, jclass /* clazz */, jlong dataSourcePtr, jobject tlsState) {
+ sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
+ tlsState = env->NewGlobalRef(tlsState);
+ return datasource->SetCustomTls(tlsState);
+}
+
jobject nativeGetIncrementalState(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) {
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
return datasource->GetIncrementalState();
}
+void nativeSetIncrementalState(JNIEnv* env, jclass /* clazz */, jlong dataSourcePtr,
+ jobject incrementalState) {
+ sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
+ incrementalState = env->NewGlobalRef(incrementalState);
+ return datasource->SetIncrementalState(incrementalState);
+}
+
const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"nativeCreate", "(Landroid/tracing/perfetto/DataSource;Ljava/lang/String;)J",
@@ -425,13 +430,16 @@
{"nativePerfettoDsTraceIterateBegin", "(J)Z", (void*)nativePerfettoDsTraceIterateBegin},
{"nativePerfettoDsTraceIterateNext", "(J)Z", (void*)nativePerfettoDsTraceIterateNext},
- {"nativePerfettoDsTraceIterateBreak", "(J)V", (void*)nativePerfettoDsTraceIterateBreak}};
+ {"nativePerfettoDsTraceIterateBreak", "(J)V", (void*)nativePerfettoDsTraceIterateBreak},
+ {"nativeGetPerfettoDsInstanceIndex", "(J)I", (void*)nativeGetPerfettoDsInstanceIndex}};
const JNINativeMethod gMethodsTracingContext[] = {
/* name, signature, funcPtr */
{"nativeFlush", "(J[[B)V", (void*)nativeFlush},
{"nativeGetCustomTls", "(J)Ljava/lang/Object;", (void*)nativeGetCustomTls},
{"nativeGetIncrementalState", "(J)Ljava/lang/Object;", (void*)nativeGetIncrementalState},
+ {"nativeSetCustomTls", "(JLjava/lang/Object;)V", (void*)nativeSetCustomTls},
+ {"nativeSetIncrementalState", "(JLjava/lang/Object;)V", (void*)nativeSetIncrementalState},
};
int register_android_tracing_PerfettoDataSource(JNIEnv* env) {
diff --git a/core/jni/android_tracing_PerfettoDataSource.h b/core/jni/android_tracing_PerfettoDataSource.h
index 209de29..fe15184 100644
--- a/core/jni/android_tracing_PerfettoDataSource.h
+++ b/core/jni/android_tracing_PerfettoDataSource.h
@@ -44,16 +44,16 @@
jobject newInstance(JNIEnv* env, void* ds_config, size_t ds_config_size,
PerfettoDsInstanceIndex inst_id);
- jobject createTlsStateGlobalRef(JNIEnv* env, PerfettoDsInstanceIndex inst_id);
- jobject createIncrementalStateGlobalRef(JNIEnv* env, PerfettoDsInstanceIndex inst_id);
-
bool TraceIterateBegin();
bool TraceIterateNext();
void TraceIterateBreak();
+ PerfettoDsInstanceIndex GetInstanceIndex();
void WritePackets(JNIEnv* env, jobjectArray packets);
jobject GetCustomTls();
+ void SetCustomTls(jobject);
jobject GetIncrementalState();
+ void SetIncrementalState(jobject);
void flushAll();
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp
new file mode 100644
index 0000000..01920de
--- /dev/null
+++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "FileSystemUtils"
+
+#include "com_android_internal_content_FileSystemUtils.h"
+
+#include <android-base/file.h>
+#include <android-base/hex.h>
+#include <android-base/unique_fd.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+
+#include <array>
+#include <fstream>
+#include <vector>
+
+using android::base::HexString;
+using android::base::ReadFullyAtOffset;
+
+namespace android {
+bool punchHoles(const char *filePath, const uint64_t offset,
+ const std::vector<Elf64_Phdr> &programHeaders) {
+ struct stat64 beforePunch;
+ if (int result = lstat64(filePath, &beforePunch); result != 0) {
+ ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno);
+ return false;
+ }
+
+ uint64_t blockSize = beforePunch.st_blksize;
+ IF_ALOGD() {
+ ALOGD("Total number of LOAD segments %zu", programHeaders.size());
+
+ ALOGD("Size before punching holes st_blocks: %" PRIu64
+ ", st_blksize: %ld, st_size: %" PRIu64 "",
+ beforePunch.st_blocks, beforePunch.st_blksize,
+ static_cast<uint64_t>(beforePunch.st_size));
+ }
+
+ android::base::unique_fd fd(open(filePath, O_RDWR | O_CLOEXEC));
+ if (!fd.ok()) {
+ ALOGE("Can't open file to punch %s", filePath);
+ return false;
+ }
+
+ // read in chunks of 64KB
+ constexpr uint64_t kChunkSize = 64 * 1024;
+
+ // malloc is used to gracefully handle oom which might occur during the allocation of buffer.
+ // allocating using new or vector here results in oom/exception on failure where as malloc will
+ // return nullptr.
+ std::unique_ptr<uint8_t, decltype(&free)> buffer(static_cast<uint8_t *>(malloc(kChunkSize)),
+ &free);
+ if (buffer == nullptr) {
+ ALOGE("Failed to allocate read buffer");
+ return false;
+ }
+
+ for (size_t index = 0; programHeaders.size() >= 2 && index < programHeaders.size() - 1;
+ index++) {
+ // find LOAD segments from program headers, calculate padding and punch holes
+ uint64_t punchOffset;
+ if (__builtin_add_overflow(programHeaders[index].p_offset, programHeaders[index].p_filesz,
+ &punchOffset)) {
+ ALOGE("Overflow occurred when adding offset and filesize");
+ return false;
+ }
+
+ uint64_t punchLen;
+ if (__builtin_sub_overflow(programHeaders[index + 1].p_offset, punchOffset, &punchLen)) {
+ ALOGE("Overflow occurred when calculating length");
+ return false;
+ }
+
+ if (punchLen < blockSize) {
+ continue;
+ }
+
+ uint64_t punchStartOffset;
+ if (__builtin_add_overflow(offset, punchOffset, &punchStartOffset)) {
+ ALOGE("Overflow occurred when calculating length");
+ return false;
+ }
+
+ uint64_t position = punchStartOffset;
+ uint64_t endPosition;
+ if (__builtin_add_overflow(position, punchLen, &endPosition)) {
+ ALOGE("Overflow occurred when calculating length");
+ return false;
+ }
+
+ // Read content in kChunkSize and verify it is zero
+ while (position <= endPosition) {
+ uint64_t uncheckedChunkEnd;
+ if (__builtin_add_overflow(position, kChunkSize, &uncheckedChunkEnd)) {
+ ALOGE("Overflow occurred when calculating uncheckedChunkEnd");
+ return false;
+ }
+
+ uint64_t readLength;
+ if (__builtin_sub_overflow(std::min(uncheckedChunkEnd, endPosition), position,
+ &readLength)) {
+ ALOGE("Overflow occurred when calculating readLength");
+ return false;
+ }
+
+ if (!ReadFullyAtOffset(fd, buffer.get(), readLength, position)) {
+ ALOGE("Failed to read content to punch holes");
+ return false;
+ }
+
+ IF_ALOGD() {
+ ALOGD("Punching holes for length:%" PRIu64 " content which should be zero: %s",
+ readLength, HexString(buffer.get(), readLength).c_str());
+ }
+
+ bool isZero = std::all_of(buffer.get(), buffer.get() + readLength,
+ [](uint8_t i) constexpr { return i == 0; });
+ if (!isZero) {
+ ALOGE("Found non zero content while trying to punch hole. Skipping operation");
+ return false;
+ }
+
+ position = uncheckedChunkEnd;
+ }
+
+ // if we have a uncompressed file which is being opened from APK, use the offset to
+ // punch native lib inside Apk.
+ int result = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, punchStartOffset,
+ punchLen);
+ if (result < 0) {
+ ALOGE("fallocate failed to punch hole, error:%d", errno);
+ return false;
+ }
+ }
+
+ IF_ALOGD() {
+ struct stat64 afterPunch;
+ if (int result = lstat64(filePath, &afterPunch); result != 0) {
+ ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno);
+ return false;
+ }
+ ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64
+ "",
+ afterPunch.st_blocks, afterPunch.st_blksize,
+ static_cast<uint64_t>(afterPunch.st_size));
+ }
+
+ return true;
+}
+
+bool punchHolesInElf64(const char *filePath, const uint64_t offset) {
+ // Open Elf file
+ Elf64_Ehdr ehdr;
+ std::ifstream inputStream(filePath, std::ifstream::in);
+
+ // If this is a zip file, set the offset so that we can read elf file directly
+ inputStream.seekg(offset);
+ // read executable headers
+ inputStream.read((char *)&ehdr, sizeof(ehdr));
+ if (!inputStream.good()) {
+ return false;
+ }
+
+ // only consider elf64 for punching holes
+ if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) {
+ ALOGW("Provided file is not ELF64");
+ return false;
+ }
+
+ // read the program headers from elf file
+ uint64_t programHeaderOffset = ehdr.e_phoff;
+ uint16_t programHeaderNum = ehdr.e_phnum;
+
+ IF_ALOGD() {
+ ALOGD("Punching holes in file: %s programHeaderOffset: %" PRIu64 " programHeaderNum: %hu",
+ filePath, programHeaderOffset, programHeaderNum);
+ }
+
+ // if this is a zip file, also consider elf offset inside a file
+ uint64_t phOffset;
+ if (__builtin_add_overflow(offset, programHeaderOffset, &phOffset)) {
+ ALOGE("Overflow occurred when calculating phOffset");
+ return false;
+ }
+ inputStream.seekg(phOffset);
+
+ std::vector<Elf64_Phdr> programHeaders;
+ for (int headerIndex = 0; headerIndex < programHeaderNum; headerIndex++) {
+ Elf64_Phdr header;
+ inputStream.read((char *)&header, sizeof(header));
+ if (!inputStream.good()) {
+ return false;
+ }
+
+ if (header.p_type != PT_LOAD) {
+ continue;
+ }
+ programHeaders.push_back(header);
+ }
+
+ return punchHoles(filePath, offset, programHeaders);
+}
+
+bool punchHolesInZip(const char *filePath, uint64_t offset, uint16_t extraFieldLen) {
+ android::base::unique_fd fd(open(filePath, O_RDWR | O_CLOEXEC));
+ if (!fd.ok()) {
+ ALOGE("Can't open file to punch %s", filePath);
+ return false;
+ }
+
+ struct stat64 beforePunch;
+ if (int result = lstat64(filePath, &beforePunch); result != 0) {
+ ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno);
+ return false;
+ }
+
+ uint64_t blockSize = beforePunch.st_blksize;
+ IF_ALOGD() {
+ ALOGD("Extra field length: %hu, Size before punching holes st_blocks: %" PRIu64
+ ", st_blksize: %ld, st_size: %" PRIu64 "",
+ extraFieldLen, beforePunch.st_blocks, beforePunch.st_blksize,
+ static_cast<uint64_t>(beforePunch.st_size));
+ }
+
+ if (extraFieldLen < blockSize) {
+ ALOGD("Skipping punching apk as extra field length is less than block size");
+ return false;
+ }
+
+ // content is preceded by extra field. Zip offset is offset of exact content.
+ // move back by extraFieldLen so that scan can be started at start of extra field.
+ uint64_t extraFieldStart;
+ if (__builtin_sub_overflow(offset, extraFieldLen, &extraFieldStart)) {
+ ALOGE("Overflow occurred when calculating start of extra field");
+ return false;
+ }
+
+ constexpr uint64_t kMaxSize = 64 * 1024;
+ // Use malloc to gracefully handle any oom conditions
+ std::unique_ptr<uint8_t, decltype(&free)> buffer(static_cast<uint8_t *>(malloc(kMaxSize)),
+ &free);
+ if (buffer == nullptr) {
+ ALOGE("Failed to allocate read buffer");
+ return false;
+ }
+
+ // Read the entire extra fields at once and punch file according to zero stretches.
+ if (!ReadFullyAtOffset(fd, buffer.get(), extraFieldLen, extraFieldStart)) {
+ ALOGE("Failed to read extra field content");
+ return false;
+ }
+
+ IF_ALOGD() {
+ ALOGD("Extra field length: %hu content near offset: %s", extraFieldLen,
+ HexString(buffer.get(), extraFieldLen).c_str());
+ }
+
+ uint64_t currentSize = 0;
+ while (currentSize < extraFieldLen) {
+ uint64_t end = currentSize;
+ // find zero ranges
+ while (end < extraFieldLen && *(buffer.get() + end) == 0) {
+ ++end;
+ }
+
+ uint64_t punchLen;
+ if (__builtin_sub_overflow(end, currentSize, &punchLen)) {
+ ALOGW("Overflow occurred when calculating punching length");
+ return false;
+ }
+
+ // Don't punch for every stretch of zero which is found
+ if (punchLen > blockSize) {
+ uint64_t punchOffset;
+ if (__builtin_add_overflow(extraFieldStart, currentSize, &punchOffset)) {
+ ALOGW("Overflow occurred when calculating punch start offset");
+ return false;
+ }
+
+ ALOGD("Punching hole in apk start: %" PRIu64 " len:%" PRIu64 "", punchOffset, punchLen);
+
+ // Punch hole for this entire stretch.
+ int result = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, punchOffset,
+ punchLen);
+ if (result < 0) {
+ ALOGE("fallocate failed to punch hole inside apk, error:%d", errno);
+ return false;
+ }
+ }
+ currentSize = end;
+ ++currentSize;
+ }
+
+ IF_ALOGD() {
+ struct stat64 afterPunch;
+ if (int result = lstat64(filePath, &afterPunch); result != 0) {
+ ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno);
+ return false;
+ }
+ ALOGD("punchHolesInApk:: Size after punching holes st_blocks: %" PRIu64
+ ", st_blksize: %ld, st_size: %" PRIu64 "",
+ afterPunch.st_blocks, afterPunch.st_blksize,
+ static_cast<uint64_t>(afterPunch.st_size));
+ }
+ return true;
+}
+
+}; // namespace android
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.h b/core/jni/com_android_internal_content_FileSystemUtils.h
new file mode 100644
index 0000000..52445e2
--- /dev/null
+++ b/core/jni/com_android_internal_content_FileSystemUtils.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <sys/types.h>
+
+namespace android {
+
+/*
+ * This function deallocates space used by zero padding at the end of LOAD segments in given
+ * uncompressed ELF file. Read ELF headers and find out the offset and sizes of LOAD segments.
+ * [fallocate(2)](http://man7.org/linux/man-pages/man2/fallocate.2.html) is used to deallocate the
+ * zero ranges at the end of LOAD segments. If ELF file is present inside of ApK/Zip file, offset to
+ * the start of the ELF file should be provided.
+ */
+bool punchHolesInElf64(const char* filePath, uint64_t offset);
+
+/*
+ * This function punches holes in zero segments of Apk file which are introduced during the
+ * alignment. Alignment tools add padding inside of extra field in local file header. punch holes in
+ * extra field for zero stretches till the actual file content.
+ */
+bool punchHolesInZip(const char* filePath, uint64_t offset, uint16_t extraFieldLen);
+
+} // namespace android
\ No newline at end of file
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 149e57a..9b8dab7 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -28,6 +28,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
+#include <sys/statfs.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
@@ -36,6 +37,7 @@
#include <memory>
+#include "com_android_internal_content_FileSystemUtils.h"
#include "core_jni_helpers.h"
#define RS_BITCODE_SUFFIX ".bc"
@@ -144,8 +146,9 @@
uint16_t method;
off64_t offset;
-
- if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc)) {
+ uint16_t extraFieldLength;
+ if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc,
+ &extraFieldLength)) {
ALOGE("Couldn't read zip entry info\n");
return INSTALL_FAILED_INVALID_APK;
}
@@ -169,6 +172,21 @@
return INSTALL_FAILED_INVALID_APK;
}
+#ifdef ENABLE_PUNCH_HOLES
+ // if library is uncompressed, punch hole in it in place
+ if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
+ ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
+ "%" PRIu64 "",
+ fileName, zipFile->getZipFileName(), offset);
+ }
+
+ // if extra field for this zip file is present with some length, possibility is that it is
+ // padding added for zip alignment. Punch holes there too.
+ if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
+ ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
+ }
+#endif // ENABLE_PUNCH_HOLES
+
return INSTALL_SUCCEEDED;
}
@@ -269,6 +287,25 @@
return INSTALL_FAILED_CONTAINER_ERROR;
}
+#ifdef ENABLE_PUNCH_HOLES
+ // punch extracted elf files as well. This will fail where compression is on (like f2fs) but it
+ // will be useful for ext4 based systems
+ struct statfs64 fsInfo;
+ int result = statfs64(localFileName, &fsInfo);
+ if (result < 0) {
+ ALOGW("Failed to stat file :%s", localFileName);
+ }
+
+ if (result == 0 && fsInfo.f_type == EXT4_SUPER_MAGIC) {
+ ALOGD("Punching extracted elf file %s on fs:%" PRIu64 "", fileName,
+ static_cast<uint64_t>(fsInfo.f_type));
+ if (!punchHolesInElf64(localFileName, 0)) {
+ ALOGW("Failed to punch extracted elf file :%s from apk : %s", fileName,
+ zipFile->getZipFileName());
+ }
+ }
+#endif // ENABLE_PUNCH_HOLES
+
ALOGV("Successfully moved %s to %s\n", localTmpFileName, localFileName);
return INSTALL_SUCCEEDED;
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 76b05ea..b3c41df 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -55,6 +55,14 @@
counter->setState(state, timestamp);
}
+static void native_copyStatesFrom(jlong nativePtrTarget, jlong nativePtrSource) {
+ battery::LongArrayMultiStateCounter *counterTarget =
+ reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtrTarget);
+ battery::LongArrayMultiStateCounter *counterSource =
+ reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtrSource);
+ counterTarget->copyStatesFrom(*counterSource);
+}
+
static void native_setValues(jlong nativePtr, jint state, jlong longArrayContainerNativePtr) {
battery::LongArrayMultiStateCounter *counter =
reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
@@ -219,6 +227,8 @@
// @CriticalNative
{"native_setState", "(JIJ)V", (void *)native_setState},
// @CriticalNative
+ {"native_copyStatesFrom", "(JJ)V", (void *)native_copyStatesFrom},
+ // @CriticalNative
{"native_setValues", "(JIJ)V", (void *)native_setValues},
// @CriticalNative
{"native_updateValues", "(JJJ)V", (void *)native_updateValues},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f743299..f55f3c7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -581,6 +581,7 @@
<protected-broadcast android:name="android.app.action.KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED" />
<protected-broadcast android:name="NotificationManagerService.TIMEOUT" />
+ <protected-broadcast android:name="com.android.server.notification.TimeToLiveHelper" />
<protected-broadcast android:name="NotificationHistoryDatabase.CLEANUP" />
<protected-broadcast android:name="ScheduleConditionProvider.EVALUATE" />
<protected-broadcast android:name="EventConditionProvider.EVALUATE" />
@@ -2593,6 +2594,13 @@
<permission android:name="android.permission.VIBRATE_ALWAYS_ON"
android:protectionLevel="signature" />
+ <!-- Allows access to system-only haptic feedback constants.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.VIBRATE_SYSTEM_CONSTANTS"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows access to the vibrator state.
<p>Protection level: signature
@hide
@@ -3889,6 +3897,13 @@
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL"
android:protectionLevel="internal|role" />
+ <!-- Allows the holder to manage and retrieve max storage limit for admin policies. This
+ permission is only grantable on rooted devices.
+ @TestAPI
+ @hide -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT"
+ android:protectionLevel="internal" />
+
<!-- Allows an application to access EnhancedConfirmationManager.
@SystemApi
@FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled")
@@ -5875,7 +5890,7 @@
<!-- Allows an application to subscribe to notifications about the nearby devices' presence
status change base on the UUIDs.
<p>Not for use by third-party applications.</p>
- @FlaggedApi("android.companion.flags.device_presence")
+ @FlaggedApi("android.companion.device_presence")
-->
<permission android:name="android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE"
android:protectionLevel="signature|privileged" />
@@ -8167,6 +8182,15 @@
<permission android:name="android.permission.SCREEN_TIMEOUT_OVERRIDE"
android:protectionLevel="signature" />
+ <!-- @SystemApi
+ @FlaggedApi("android.security.fsverity_api")
+ Allows app to setup fs-verity through FileIntegrityManager.
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.SETUP_FSVERITY"
+ android:protectionLevel="signature|privileged"/>
+
<!-- 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
index b042c39..8f7bbb5 100644
--- a/core/res/res/drawable/ic_private_profile_badge.xml
+++ b/core/res/res/drawable/ic_private_profile_badge.xml
@@ -20,6 +20,10 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:pathData="M5,3H19C20.1,3 21,3.9 21,5V19C21,20.1 20.1,21 19,21H5C3.9,21 3,20.1 3,19V5C3,3.9 3.9,3 5,3ZM13.5,15.501L12.93,12.271C13.57,11.941 14,11.271 14,10.501C14,9.401 13.1,8.501 12,8.501C10.9,8.501 10,9.401 10,10.501C10,11.271 10.43,11.941 11.07,12.271L10.5,15.501H13.5Z"
+ android:pathData="M12,2L4,5V11.09C4,16.14 7.41,20.85 12,22C16.59,20.85 20,16.14 20,11.09V5L12,2ZM15,15V17H13V18H11V12.84C9.56,12.41 8.5,11.09 8.5,9.5C8.5,7.57 10.07,6 12,6C13.93,6 15.5,7.57 15.5,9.5C15.5,11.08 14.44,12.41 13,12.84V15H15Z"
+ android:fillColor="#3C4043"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M12,11C12.828,11 13.5,10.328 13.5,9.5C13.5,8.672 12.828,8 12,8C11.172,8 10.5,8.672 10.5,9.5C10.5,10.328 11.172,11 12,11Z"
android:fillColor="#3C4043"/>
</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
index 5f1f1b7..4143c3b 100644
--- a/core/res/res/drawable/ic_private_profile_icon_badge.xml
+++ b/core/res/res/drawable/ic_private_profile_icon_badge.xml
@@ -24,8 +24,12 @@
android:scaleY=".66"
android:translateX="42"
android:translateY="42">
- <path
- android:pathData="M5,3H19C20.1,3 21,3.9 21,5V19C21,20.1 20.1,21 19,21H5C3.9,21 3,20.1 3,19V5C3,3.9 3.9,3 5,3ZM13.5,15.501L12.93,12.271C13.57,11.941 14,11.271 14,10.501C14,9.401 13.1,8.501 12,8.501C10.9,8.501 10,9.401 10,10.501C10,11.271 10.43,11.941 11.07,12.271L10.5,15.501H13.5Z"
- android:fillColor="#3C4043"/>
+ <path
+ android:pathData="M12,2L4,5V11.09C4,16.14 7.41,20.85 12,22C16.59,20.85 20,16.14 20,11.09V5L12,2ZM15,15V17H13V18H11V12.84C9.56,12.41 8.5,11.09 8.5,9.5C8.5,7.57 10.07,6 12,6C13.93,6 15.5,7.57 15.5,9.5C15.5,11.08 14.44,12.41 13,12.84V15H15Z"
+ android:fillColor="#3C4043"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M12,11C12.828,11 13.5,10.328 13.5,9.5C13.5,8.672 12.828,8 12,8C11.172,8 10.5,8.672 10.5,9.5C10.5,10.328 11.172,11 12,11Z"
+ android:fillColor="#3C4043"/>
</group>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_spot_anchor_vector.xml b/core/res/res/drawable/pointer_spot_anchor_vector.xml
index 54de2ae..89990b8 100644
--- a/core/res/res/drawable/pointer_spot_anchor_vector.xml
+++ b/core/res/res/drawable/pointer_spot_anchor_vector.xml
@@ -22,4 +22,8 @@
<path android:fillColor="#ADC6E7" android:pathData="M12 3c-4.963 0-9 4.038-9 9 0 4.963 4.037 9 9 9s9-4.037 9-9c0-4.962-4.037-9-9-9m0 17c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8" />
<path android:fillColor="#ADC6E7" android:pathData="M12 5c-3.859 0-7 3.14-7 7s3.141 7 7 7 7-3.141 7-7-3.141-7-7-7m0 13c-3.309 0-6-2.691-6-6s2.691-6 6-6 6 2.691 6 6-2.691 6-6 6" />
</group>
+ <path
+ android:pathData="M12 4a8 8 0 1 0 0 16 8 8 0 0 0 0 -16m0 15a7 7 0 1 1 0 -14 7 7 0 0 1 0 14"
+ android:fillColor="#99FFFFFF"
+ android:fillType="evenOdd"/>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_spot_hover_vector.xml b/core/res/res/drawable/pointer_spot_hover_vector.xml
index ef596c4..4bf5fbc 100644
--- a/core/res/res/drawable/pointer_spot_hover_vector.xml
+++ b/core/res/res/drawable/pointer_spot_hover_vector.xml
@@ -22,4 +22,7 @@
<path android:fillColor="#ADC6E7" android:pathData="M12 3c-4.963 0-9 4.038-9 9 0 4.963 4.037 9 9 9s9-4.037 9-9c0-4.962-4.037-9-9-9m0 17c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8" />
<path android:fillColor="#ADC6E7" android:pathData="M12 7c-2.757 0-5 2.243-5 5s2.243 5 5 5 5-2.243 5-5-2.243-5-5-5m0 9c-2.206 0-4-1.794-4-4s1.794-4 4-4 4 1.794 4 4-1.794 4-4 4" />
</group>
+ <path
+ android:pathData="M12 3.998a8.002 8.002 0 1 0 0 16.004 8.002 8.002 0 0 0 0 -16.004m0 13.004a5.002 5.002 0 1 1 0 -10.004 5.002 5.002 0 0 1 0 10.004"
+ android:fillColor="#99FFFFFF"/>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_spot_touch_vector.xml b/core/res/res/drawable/pointer_spot_touch_vector.xml
index afd2956..a25ffe0 100644
--- a/core/res/res/drawable/pointer_spot_touch_vector.xml
+++ b/core/res/res/drawable/pointer_spot_touch_vector.xml
@@ -21,4 +21,7 @@
<path
android:fillColor="#ADC6E7"
android:pathData="M21 12c0-4.963-4.038-9-9-9s-9 4.037-9 9 4.038 9 9 9 9-4.037 9-9m-9 8c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8" />
+ <path
+ android:pathData="M12 12m-8 0a8 8 0 1 1 16 0a8 8 0 1 1 -16 0"
+ android:fillColor="#99FFFFFF"/>
</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
index 429070e..958a4d7 100644
--- a/core/res/res/drawable/stat_sys_private_profile_status.xml
+++ b/core/res/res/drawable/stat_sys_private_profile_status.xml
@@ -20,6 +20,10 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
+ android:pathData="M12,2L4,5V11.09C4,16.14 7.41,20.85 12,22C16.59,20.85 20,16.14 20,11.09V5L12,2ZM15,15V17H13V18H11V12.84C9.56,12.41 8.5,11.09 8.5,9.5C8.5,7.57 10.07,6 12,6C13.93,6 15.5,7.57 15.5,9.5C15.5,11.08 14.44,12.41 13,12.84V15H15Z"
android:fillColor="@android:color/white"
- android:pathData="M5,3H19C20.1,3 21,3.9 21,5V19C21,20.1 20.1,21 19,21H5C3.9,21 3,20.1 3,19V5C3,3.9 3.9,3 5,3ZM13.5,15.501L12.93,12.271C13.57,11.941 14,11.271 14,10.501C14,9.401 13.1,8.501 12,8.501C10.9,8.501 10,9.401 10,10.501C10,11.271 10.43,11.941 11.07,12.271L10.5,15.501H13.5Z"/>
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M12,11C12.828,11 13.5,10.328 13.5,9.5C13.5,8.672 12.828,8 12,8C11.172,8 10.5,8.672 10.5,9.5C10.5,10.328 11.172,11 12,11Z"
+ android:fillColor="@android:color/white"/>
</vector>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index be1c939..6f06d80 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -80,6 +80,7 @@
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:importantForAccessibility="no"
+ android:focusable="false"
/>
<include layout="@layout/notification_expand_button"
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 710a70a..64227d8 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -55,6 +55,7 @@
android:layout_height="match_parent"
android:layout_gravity="start"
android:importantForAccessibility="no"
+ android:focusable="false"
/>
<LinearLayout
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index df32d30..8a94c48 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -54,6 +54,7 @@
android:layout_height="match_parent"
android:layout_gravity="start"
android:importantForAccessibility="no"
+ android:focusable="false"
/>
<LinearLayout
diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml
index 3e82bd1..a83d923 100644
--- a/core/res/res/layout/notification_template_material_messaging.xml
+++ b/core/res/res/layout/notification_template_material_messaging.xml
@@ -67,6 +67,7 @@
android:layout_height="match_parent"
android:layout_gravity="start"
android:importantForAccessibility="no"
+ android:focusable="false"
/>
<!--
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 63cd6b9..0841861 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -259,7 +259,7 @@
<string name="global_action_emergency" msgid="1387617624177105088">"Noodgeval"</string>
<string name="global_action_bug_report" msgid="5127867163044170003">"Foutverslag"</string>
<string name="global_action_logout" msgid="6093581310002476511">"Beëindig sessie"</string>
- <string name="global_action_screenshot" msgid="2610053466156478564">"Skermkiekie"</string>
+ <string name="global_action_screenshot" msgid="2610053466156478564">"Skermskoot"</string>
<string name="bugreport_title" msgid="8549990811777373050">"Foutverslag"</string>
<string name="bugreport_message" msgid="5212529146119624326">"Dit sal inligting oor die huidige toestand van jou toestel insamel om as \'n e-posboodskap te stuur. Dit sal \'n tydjie neem vandat die foutverslag begin is totdat dit reg is om gestuur te word; wees asseblief geduldig."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"Interaktiewe verslag"</string>
@@ -1426,7 +1426,7 @@
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Tik om taal en uitleg te kies"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
- <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Wys bo-oor ander programme"</string>
+ <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Wys bo-oor ander apps"</string>
<string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> word bo-oor ander programme gewys"</string>
<string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> wys bo-oor ander programme"</string>
<string name="alert_windows_notification_message" msgid="6538171456970725333">"As jy nie wil hê dat <xliff:g id="NAME">%s</xliff:g> hierdie kenmerk gebruik nie, tik om instellings oop te maak en skakel dit af."</string>
@@ -1929,12 +1929,9 @@
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Bestuur deur <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aan"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Af"</string>
- <!-- no translation found for zen_mode_trigger_summary_divider_text (7461583466043698862) -->
- <skip />
- <!-- no translation found for zen_mode_trigger_summary_range_symbol_combination (1804900738798069619) -->
- <skip />
- <!-- no translation found for zen_mode_trigger_event_calendar_any (2086784607921121803) -->
- <skip />
+ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
+ <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
+ <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Enige kalender"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> demp sekere klanke"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Daar is \'n interne probleem met jou toestel en dit sal dalk onstabiel wees totdat jy \'n fabriekterugstelling doen."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Daar is \'n interne probleem met jou toestel. Kontak jou vervaardiger vir besonderhede."</string>
@@ -1973,7 +1970,7 @@
<string name="default_notification_channel_label" msgid="3697928973567217330">"Ongekategoriseer"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Jy stel die belangrikheid van hierdie kennisgewings."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Dit is belangrik as gevolg van die mense wat betrokke is."</string>
- <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Gepasmaakte programkennisgewing"</string>
+ <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Gepasmaakte appkennisgewing"</string>
<string name="user_creation_account_exists" msgid="2239146360099708035">"Laat <xliff:g id="APP">%1$s</xliff:g> toe om \'n nuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> te skep (\'n gebruiker met hierdie rekening bestaan reeds)?"</string>
<string name="user_creation_adding" msgid="7305185499667958364">"Laat <xliff:g id="APP">%1$s</xliff:g> toe om \'n nuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> te skep?"</string>
<string name="supervised_user_creation_label" msgid="6884904353827427515">"Voeg gebruiker onder toesig by"</string>
@@ -2179,7 +2176,7 @@
<string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Kitsinstellings"</string>
<string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Kragdialoog"</string>
<string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"Sluitskerm"</string>
- <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Skermkiekie"</string>
+ <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Skermskoot"</string>
<string name="accessibility_system_action_headset_hook_label" msgid="8524691721287425468">"Kopstuk haak"</string>
<string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"Toeganklikheidkortpad op skerm"</string>
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Toeganklikheidkortpadkieser op skerm"</string>
@@ -2397,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Toets"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Gemeenskaplik"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Appinhoud is weens sekuriteit van skermdeling verberg"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Outomaties aan satelliet gekoppel"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Jy kan boodskappe stuur en ontvang sonder ’n selfoon- of wi-fi-netwerk"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Maak Boodskappe oop"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 799a7ab..86df650 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1894,7 +1894,7 @@
<string name="clone_profile_label_badge" msgid="1871997694718793964">"<xliff:g id="LABEL">%1$s</xliff:g>ን አባዛ"</string>
<string name="private_profile_label_badge" msgid="1712086003787839183">"የግል <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"ከመንቀል በፊት ፒን ጠይቅ"</string>
- <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ከመንቀል በፊት የማስከፈቻ ስርዓተ-ጥለት ጠይቅ"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ከመንቀል በፊት የማስከፈቻ ሥርዓተ-ጥለት ጠይቅ"</string>
<string name="lock_to_app_unlock_password" msgid="9126722403506560473">"ከመንቀል በፊት የይለፍ ቃል ጠይቅ"</string>
<string name="package_installed_device_owner" msgid="7035926868974878525">"በእርስዎ አስተዳዳሪ ተጭኗል"</string>
<string name="package_updated_device_owner" msgid="7560272363805506941">"በእርስዎ አስተዳዳሪ ተዘምኗል"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"ሙከራ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"የጋራ"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ለደኅንነት ሲባል የመተግበሪያ ይዘት ከማያ ገጽ ማጋራት ተደብቋል"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"ከሳተላይት ጋር በራስ-ሰር ተገናኝቷል"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"ያለ ሞባይል ወይም የWi-Fi አውታረ መረብ መልዕክቶችን መላክ እና መቀበል ይችላሉ"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"መልዕክቶች ይክፈቱ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index fad2cd6..185c3c6 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -195,10 +195,10 @@
<string name="low_memory" product="default" msgid="2539532364144025569">"مساحة تخزين الهاتف ممتلئة. احذف بعض الملفات لإخلاء مساحة."</string>
<string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{تم تثبيت مرجع التصديق.}zero{تم تثبيت مراجع التصديق.}two{تم تثبيت مرجعَي التصديق.}few{تم تثبيت مراجع التصديق.}many{تم تثبيت مراجع التصديق.}other{تم تثبيت مراجع التصديق.}}"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"بواسطة جهة خارجية غير معلومة"</string>
- <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"بواسطة مشرف الملف الشخصي للعمل"</string>
+ <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"بواسطة مشرف ملف العمل"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"بواسطة <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
- <string name="work_profile_deleted" msgid="5891181538182009328">"تم حذف الملف الشخصي للعمل."</string>
- <string name="work_profile_deleted_details" msgid="3773706828364418016">"تطبيق المشرف للملف الشخصي للعمل مفقود أو تالف لذا تم حذف الملف الشخصي للعمل والبيانات ذات الصلة. اتصل بالمشرف للحصول على المساعدة."</string>
+ <string name="work_profile_deleted" msgid="5891181538182009328">"تم حذف ملف العمل."</string>
+ <string name="work_profile_deleted_details" msgid="3773706828364418016">"تطبيق المشرف لملف العمل مفقود أو تالف لذا تم حذف ملف العمل والبيانات ذات الصلة. اتصل بالمشرف للحصول على المساعدة."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"لم يعد ملفك الشخصي للعمل متاحًا على هذا الجهاز"</string>
<string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"تم إجراء محاولات كثيرة جدًا لإدخال كلمة المرور"</string>
<string name="device_ownership_relinquished" msgid="4080886992183195724">"تنازل المشرف عن الجهاز للاستخدام الشخصي"</string>
@@ -218,9 +218,9 @@
<string name="factory_reset_warning" msgid="6858705527798047809">"سيتم محو بيانات جهازك."</string>
<string name="factory_reset_message" msgid="2657049595153992213">"تعذّر استخدام تطبيق المشرف. سيتم محو بيانات جهازك الآن.\n\nإذا كانت لديك أسئلة، اتصل بمشرف مؤسستك."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"تم إيقاف الطباعة بواسطة <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
- <string name="personal_apps_suspension_title" msgid="7561416677884286600">"تفعيل الملف الشخصي للعمل"</string>
+ <string name="personal_apps_suspension_title" msgid="7561416677884286600">"تفعيل ملف العمل"</string>
<string name="personal_apps_suspension_text" msgid="6115455688932935597">"تم حظر تطبيقاتك الشخصية إلى أن تفعِّل ملفك الشخصي للعمل."</string>
- <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"سيتم حظر التطبيقات الشخصية في <xliff:g id="DATE">%1$s</xliff:g> في <xliff:g id="TIME">%2$s</xliff:g>. لا يسمح مشرف تكنولوجيا المعلومات في مؤسستك بإيقاف الملف الشخصي للعمل أكثر من <xliff:g id="NUMBER">%3$d</xliff:g> يوم."</string>
+ <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"سيتم حظر التطبيقات الشخصية في <xliff:g id="DATE">%1$s</xliff:g> في <xliff:g id="TIME">%2$s</xliff:g>. لا يسمح مشرف تكنولوجيا المعلومات في مؤسستك بإيقاف ملف العمل أكثر من <xliff:g id="NUMBER">%3$d</xliff:g> يوم."</string>
<string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"تفعيل"</string>
<string name="work_profile_telephony_paused_title" msgid="7690804479291839519">"المكالمات والرسائل غير مفعّلة"</string>
<string name="work_profile_telephony_paused_text" msgid="8065762301100978221">"لقد أوقفت تطبيقات العمل مؤقتًا. لن تتلقّى مكالمات هاتفية أو رسائل نصية."</string>
@@ -312,9 +312,9 @@
<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="managed_profile_label" msgid="7316778766973512382">"التبديل إلى الملف الشخصي للعمل"</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>
+ <string name="managed_profile_app_label" msgid="367401088383965725">"التبديل إلى تطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" في ملف العمل"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"جهات الاتصال"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"الوصول إلى جهات اتصالك"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"الموقع الجغرافي"</string>
@@ -708,10 +708,10 @@
<string name="face_acquired_too_dark" msgid="8539853432479385326">"الإضاءة غير كافية"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"يُرجى إبعاد الهاتف عنك."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"يُرجى تقريب الهاتف منك"</string>
- <string name="face_acquired_too_high" msgid="8278815780046368576">"يُرجى رفع الهاتف للأعلى"</string>
- <string name="face_acquired_too_low" msgid="4075391872960840081">"يُرجى خفض الهاتف للأسفل"</string>
- <string name="face_acquired_too_right" msgid="6245286514593540859">"يُرجى تحريك الهاتف لجهة اليسار"</string>
- <string name="face_acquired_too_left" msgid="9201762240918405486">"يُرجى تحريك الهاتف لجهة اليمين"</string>
+ <string name="face_acquired_too_high" msgid="8278815780046368576">"ارفع الهاتف للأعلى"</string>
+ <string name="face_acquired_too_low" msgid="4075391872960840081">"خفّض الهاتف للأسفل"</string>
+ <string name="face_acquired_too_right" msgid="6245286514593540859">"حرِّك الهاتف لجهة اليسار"</string>
+ <string name="face_acquired_too_left" msgid="9201762240918405486">"حرِّك الهاتف لجهة اليمين"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"يُرجى النظر إلى جهازك مباشرة أكثر."</string>
<string name="face_acquired_not_detected" msgid="1057966913397548150">"ارفع هاتفك إلى مستوى العينَين لأنّه تتعذّر رؤية وجهك"</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"حركة أكثر من اللازم. يُرجى حمل الهاتف بثبات."</string>
@@ -999,7 +999,7 @@
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحيح!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"أعد المحاولة"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"أعد المحاولة"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"فتح قفل جميع الميزات والبيانات"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"فتح القفل للوصول إلى جميع الميزات والبيانات"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"تم تجاوز الحد الأقصى لعدد محاولات فتح الجهاز بالتعرف على الوجه"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"لا تتوفر شريحة SIM."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"لا تتوفر شريحة SIM في الجهاز اللوحي."</string>
@@ -1401,7 +1401,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"تم اكتشاف ملحق صوتي تناظري"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"الجهاز الذي تم توصيله بالهاتف غير متوافق معه. انقر للحصول على المزيد من المعلومات."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"تم توصيل USB لتصحيح أخطاء الجهاز"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"انقر لإيقاف تصحيح أخطاء الجهاز عبر USB."</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"انقر لإيقاف تصحيح أخطاء الجهاز عبر USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"اختيار إيقاف تصحيح أخطاء USB."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"تم تفعيل ميزة \"تصحيح الأخطاء اللاسلكي\"."</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"انقر لإيقاف ميزة \"تصحيح الأخطاء اللاسلكي\"."</string>
@@ -1660,7 +1660,7 @@
<string name="media_route_button_content_description" msgid="2299223698196869956">"البث"</string>
<string name="media_route_chooser_title" msgid="6646594924991269208">"الاتصال بجهاز"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"بث الشاشة على الجهاز"</string>
- <string name="media_route_chooser_searching" msgid="6119673534251329535">"جارٍ البحث عن الأجهزة…"</string>
+ <string name="media_route_chooser_searching" msgid="6119673534251329535">"جارٍ البحث عن أجهزة…"</string>
<string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"الإعدادات"</string>
<string name="media_route_controller_disconnect" msgid="7362617572732576959">"قطع الاتصال"</string>
<string name="media_route_status_scanning" msgid="8045156315309594482">"البحث عن الشبكات..."</string>
@@ -1898,7 +1898,7 @@
<string name="clone_profile_label_badge" msgid="1871997694718793964">"نسخة طبق الأصل عن \"<xliff:g id="LABEL">%1$s</xliff:g>\""</string>
<string name="private_profile_label_badge" msgid="1712086003787839183">"ملف شخصي خاص على <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"طلب إدخال رقم التعريف الشخصي قبل إزالة التثبيت"</string>
- <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"طلب إدخال النقش الخاص بإلغاء القفل قبل إزالة التثبيت"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"طلب إدخال نقش فتح القفل قبل إزالة التثبيت"</string>
<string name="lock_to_app_unlock_password" msgid="9126722403506560473">"طلب إدخال كلمة المرور قبل إزالة التثبيت"</string>
<string name="package_installed_device_owner" msgid="7035926868974878525">"تم التثبيت بواسطة المشرف"</string>
<string name="package_updated_device_owner" msgid="7560272363805506941">"تم التحديث بواسطة المشرف"</string>
@@ -1906,7 +1906,7 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"حسنًا"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"يؤدي استخدام ميزة \"توفير شحن البطارية\" إلى تفعيل وضع \"المظهر الداكن\" وتقييد أو إيقاف الأنشطة في الخلفية وبعض التأثيرات المرئية وميزات معيّنة وبعض اتصالات الشبكات."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"يؤدي استخدام ميزة \"توفير شحن البطارية\" إلى تفعيل وضع \"المظهر الداكن\" وتقييد أو إيقاف الأنشطة في الخلفية وبعض التأثيرات المرئية وميزات معيّنة وبعض اتصالات الشبكات."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"للمساعدة في خفض استخدام البيانات، تمنع ميزة \"توفير البيانات\" بعض التطبيقات من إرسال البيانات وتلقّيها في الخلفية. يمكن للتطبيقات المتاحة لديك الآن استخدام البيانات، ولكن لا يمكنها الإكثار من ذلك. وهذا يعني أن الصور مثلاً لا تظهر حتى تنقر عليها."</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"للمساعدة في خفض استخدام البيانات، تمنع ميزة \"توفير البيانات\" بعض التطبيقات من إرسال البيانات وتلقّيها في الخلفية. يمكن للتطبيقات المتاحة لديك الآن استخدام البيانات، ولكن بمعدّل أقل. وهذا يعني أن الصور مثلاً لن تظهر حتى تنقر عليها."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"هل تريد تفعيل ميزة \"توفير البيانات\"؟"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"تفعيل"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{لمدة دقيقة واحدة (حتى {formattedTime})}zero{لمدة # دقيقة (حتى {formattedTime})}two{لمدة دقيقتين (حتى {formattedTime})}few{لمدة # دقائق (حتى {formattedTime})}many{لمدة # دقيقة (حتى {formattedTime})}other{لمدة # دقيقة (حتى {formattedTime})}}"</string>
@@ -1948,7 +1948,7 @@
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"تم تغيير طلب SS إلى طلب USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"تم التغيير إلى طلب SS الجديد."</string>
<string name="notification_phishing_alert_content_description" msgid="494227305355958790">"تنبيه بشأن تصيّد احتيالي"</string>
- <string name="notification_work_profile_content_description" msgid="5296477955677725799">"الملف الشخصي للعمل"</string>
+ <string name="notification_work_profile_content_description" msgid="5296477955677725799">"ملف العمل"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"تمّ تفعيل التنبيه"</string>
<string name="notification_verified_content_description" msgid="6401483602782359391">"تم التحقّق من المتّصل"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"توسيع"</string>
@@ -2027,8 +2027,8 @@
<string name="new_sms_notification_title" msgid="6528758221319927107">"لديك رسائل جديدة"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"فتح تطبيق الرسائل القصيرة SMS للعرض"</string>
<string name="profile_encrypted_title" msgid="9001208667521266472">"قد تكون بعض الوظائف مُقيّدة."</string>
- <string name="profile_encrypted_detail" msgid="5279730442756849055">"تم قفل الملف الشخصي للعمل."</string>
- <string name="profile_encrypted_message" msgid="1128512616293157802">"انقر لإلغاء قفل الملف الشخصي للعمل"</string>
+ <string name="profile_encrypted_detail" msgid="5279730442756849055">"تم قفل ملف العمل."</string>
+ <string name="profile_encrypted_message" msgid="1128512616293157802">"انقر لإلغاء قفل ملف العمل"</string>
<string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"تم الاتصال بـ <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
<string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"انقر لعرض الملفات"</string>
<string name="pin_target" msgid="8036028973110156895">"تثبيت"</string>
@@ -2213,7 +2213,7 @@
<string name="resolver_no_personal_apps_available" msgid="6284837227019594881">"ما مِن تطبيقات شخصية."</string>
<string name="miniresolver_open_work" msgid="6286176185835401931">"هل تريد فتح تطبيق \"<xliff:g id="APP">%s</xliff:g>\" في ملفك الشخصي للعمل؟"</string>
<string name="miniresolver_open_in_personal" msgid="807427577794490375">"هل تريد فتح المحتوى في تطبيق \"<xliff:g id="APP">%s</xliff:g>\" في الملف الشخصي؟"</string>
- <string name="miniresolver_open_in_work" msgid="941341494673509916">"هل تريد فتح المحتوى في تطبيق \"<xliff:g id="APP">%s</xliff:g>\" في الملف الشخصي للعمل؟"</string>
+ <string name="miniresolver_open_in_work" msgid="941341494673509916">"هل تريد فتح المحتوى في تطبيق \"<xliff:g id="APP">%s</xliff:g>\" في ملف العمل؟"</string>
<string name="miniresolver_call_in_work" msgid="528779988307529039">"هل تريد الاتصال من تطبيق العمل؟"</string>
<string name="miniresolver_switch_to_work" msgid="1042640606122638596">"هل تريد الانتقال إلى تطبيق العمل؟"</string>
<string name="miniresolver_call_information" msgid="6739417525304184083">"تسمح لك مؤسستك بإجراء المكالمات من تطبيقات العمل فقط."</string>
@@ -2398,8 +2398,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"ملف شخصي تجريبي"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ملف شخصي مشترك"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"تم إخفاء محتوى التطبيق بعد تفعيل ميزة \"مشاركة الشاشة\" للحفاظ على أمانك"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"تم الاتصال تلقائيًا بالقمر الصناعي"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"يمكنك إرسال الرسائل واستلامها بدون شبكة الجوّال أو شبكة Wi-Fi."</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"فتح تطبيق \"الرسائل\""</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 8b3f832..618c581 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1726,7 +1726,7 @@
<string name="accessibility_service_warning_description" msgid="291674995220940133">"আপোনাক সাধ্য সুবিধাৰ প্ৰয়োজনসমূহৰ জৰিয়তে সহায় কৰা এপ্সমূহৰ বাবে সম্পূর্ণ নিয়ন্ত্ৰণৰ সুবিধাটো সঠিক যদিও অধিকাংশ এপৰ বাবে এয়া সঠিক নহয়।"</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"চাওক আৰু স্ক্ৰীন নিয়ন্ত্ৰণ কৰক"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ই স্ক্ৰীনত থকা আটাইখিনি সমল পঢ়িব পাৰে আৰু অন্য এপ্সমূহৰ ওপৰত সমল প্ৰদর্শন কৰিব পাৰে।"</string>
- <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"কার্যসমূহ চাওক আৰু কৰক"</string>
+ <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"কার্যসমূহ চোৱা আৰু কৰা"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ই আপুনি কোনো এপ্ বা হার্ডৱেৰ ছেন্সৰৰ সৈতে কৰা ভাব-বিনিময় আৰু আপোনাৰ হৈ অন্য কোনো লোকে এপৰ সৈতে কৰা ভাব-বিনিময় ট্ৰেক কৰিব পাৰে।"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"অনুমতি দিয়ক"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"অস্বীকাৰ কৰক"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"পৰীক্ষা"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"শ্বেয়াৰ কৰা"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"সুৰক্ষাৰ বাবে এপৰ সমল স্ক্ৰীণ শ্বেয়াৰ কৰাৰ পৰা লুকুৱাই ৰখা হৈছে"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"উপগ্ৰহৰ সৈতে স্বয়ংক্ৰিয়ভাৱে সংযুক্ত হৈছে"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"আপুনি ম’বাইল বা ৱাই-ফাই নেটৱৰ্কৰ জৰিয়তে পাঠ বাৰ্তা পঠিয়াব বা লাভ কৰিব পাৰে"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages খোলক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index c0b886b..643c5cb 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Kommunal"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Güvənlik üçün tətbiq kontenti ekran paylaşımından gizlədildi"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Peykə avtomatik qoşulub"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Mobil və ya Wi-Fi şəbəkəsi olmadan mesaj göndərə və qəbul edə bilərsiniz"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Mesajı açın"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index a275cab..06b6a13 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1223,7 +1223,7 @@
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"Izmeni"</string>
<string name="whichSendApplication" msgid="4143847974460792029">"Delite"</string>
<string name="whichSendApplicationNamed" msgid="4470386782693183461">"Delite pomoću aplikacije %1$s"</string>
- <string name="whichSendApplicationLabel" msgid="7467813004769188515">"Deli"</string>
+ <string name="whichSendApplicationLabel" msgid="7467813004769188515">"Deljenje"</string>
<string name="whichSendToApplication" msgid="77101541959464018">"Pošaljite pomoću:"</string>
<string name="whichSendToApplicationNamed" msgid="3385686512014670003">"Pošaljite pomoću: %1$s"</string>
<string name="whichSendToApplicationLabel" msgid="3543240188816513303">"Pošalji"</string>
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je skriven za deljenje sadržaja ekrana zbog bezbednosti"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automatski povezano sa satelitom"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Možete da šaljete i primate poruke bez mobilne ili WiFi mreže"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvori Messages"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index c0d930f..43c73d1 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -2396,8 +2396,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Тэставы"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Супольны"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Змесціва праграмы выключана з абагульвання экрана ў мэтах бяспекі"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Аўтаматычна падключана да сістэм спадарожнікавай сувязі"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Вы можаце адпраўляць і атрымліваць паведамленні без доступу да мабільнай сеткі або Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Адкрыць Паведамленні"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 5c793e5..181e612 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Тестване"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Общи"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Съдържанието на приложението е скрито от функцията за споделяне на екрана от съображения за сигурност"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Автоматично установена връзка със сателит"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Можете да изпращате и получавате съобщения без мобилна или Wi-Fi мрежа"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Отваряне на Messages"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 8946607..9583461 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1743,7 +1743,7 @@
<string name="color_inversion_feature_name" msgid="2672824491933264951">"কালার ইনভার্সন"</string>
<string name="color_correction_feature_name" msgid="7975133554160979214">"রঙ সংশোধন করা"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"এক হাতে ব্যবহার করার মোড"</string>
- <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"অতিরিক্ত কম ব্রাইটনেস"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"অতিরিক্ত কম উজ্জ্বলতা"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"হিয়ারিং ডিভাইস"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> চালু করা হয়েছে।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> বন্ধ করা হয়েছে।"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"পরীক্ষা"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"কমিউনাল"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"নিরাপত্তার জন্য স্ক্রিন শেয়ার করা থেকে লুকানো অ্যাপের কন্টেন্ট"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"স্যাটেলাইটের সাথে অটোমেটিক কানেক্ট করা হয়েছে"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"আপনি কোনও মেবাইল বা ওয়াই-ফাই নেটওয়ার্ক ছাড়াই মেসেজ পাঠাতে ও পেতে পারবেন"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages খুলুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 2d08a22..170f84e 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Testno"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Opće"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je sakriven od dijeljenja ekrana radi sigurnosti"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automatski je povezano sa satelitom"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Možete slati i primati poruke bez mobilne ili WiFi mreže"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvorite Messages"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 263d129..ae4fbcf 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -842,10 +842,10 @@
<string name="policylab_forceLock" msgid="7360335502968476434">"Bloquejar la pantalla"</string>
<string name="policydesc_forceLock" msgid="1008844760853899693">"Controla com i quan es bloqueja la pantalla."</string>
<string name="policylab_wipeData" msgid="1359485247727537311">"Esborrar totes les dades"</string>
- <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Esborra les dades de la tauleta sense avisar, i restableix les dades de fàbrica."</string>
+ <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Esborra les dades de la tauleta sense avisar mitjançant el restabliment de les dades de fàbrica."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Suprimeix les dades del dispositiu Android TV sense previ avís mitjançant el restabliment de les dades de fàbrica."</string>
<string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Esborra les dades del sistema d\'informació i entreteniment sense avisar mitjançant el restabliment de les dades de fàbrica."</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Esborra les dades del telèfon sense avisar, i restableix les dades de fàbrica."</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Esborra les dades del telèfon sense avisar mitjançant el restabliment de les dades de fàbrica."</string>
<string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Esborra les dades del perfil"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Esborrar les dades de l\'usuari"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Esborra les dades de l\'usuari desades a la tauleta sense avisar-ne."</string>
@@ -996,7 +996,7 @@
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcte!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Torna-ho a provar"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Torna-ho a provar"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"Desbl. per accedir a totes les funcions i dades"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"Desbloqueja per accedir a les funcions i dades"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"S\'ha superat el nombre màxim d\'intents de Desbloqueig facial"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"No hi ha cap SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"No hi ha cap SIM a la tauleta."</string>
@@ -2032,7 +2032,7 @@
<string name="pin_specific_target" msgid="7824671240625957415">"Fixa <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="unpin_target" msgid="3963318576590204447">"No fixis"</string>
<string name="unpin_specific_target" msgid="3859828252160908146">"No fixis <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="app_info" msgid="6113278084877079851">"Informació de l\'aplicació"</string>
+ <string name="app_info" msgid="6113278084877079851">"Informació de l\'app"</string>
<string name="negative_duration" msgid="1938335096972945232">"-<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="6577581216125805905">"S\'està iniciant la demostració…"</string>
<string name="demo_restarting_message" msgid="1160053183701746766">"S\'està restablint el dispositiu…"</string>
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Prova"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Compartit"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contingut de l\'aplicació amagat de la compartició de pantalla per motius de seguretat"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"S\'ha connectat automàticament a un satèl·lit"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Pots enviar i rebre missatges sense una xarxa mòbil o Wi‑Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Obre Missatges"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index a8ad543..4318608 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -833,16 +833,16 @@
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Sledovat počet nesprávných hesel zadaných při odemykání obrazovky a uzamknout tablet nebo vymazat z tabletu všechna data, pokud bylo zadáno příliš mnoho nesprávných hesel."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Sledovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud jich bude zadáno příliš mnoho, uzamknout zařízení Android TV nebo z něj vymazat všechna data."</string>
<string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorovat počet nesprávných hesel zadaných při odemykání obrazovky a uzamknout informační a zábavní systém nebo vymazat veškerá data v informačním a zábavním systému, pokud je zadáno příliš mnoho nesprávných hesel."</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Sledovat počet nesprávných hesel zadaných při odemykání obrazovky a uzamknout telefon nebo vymazat z telefonu všechna data, pokud bylo zadáno příliš mnoho nesprávných hesel."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Sleduje počet nesprávných hesel zadaných při odemykání obrazovky a uzamkne telefon nebo vymaže z telefonu všechna data, pokud bylo zadáno příliš mnoho nesprávných hesel."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud je zadáno příliš mnoho nesprávných hesel, uzamknout tablet nebo vymazat veškerá data uživatele."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Sledovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud jich bude zadáno příliš mnoho, uzamknout zařízení Android TV nebo z něj vymazat všechna data tohoto uživatele."</string>
<string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud je zadáno příliš mnoho nesprávných hesel, uzamknout informační a zábavní systém nebo vymazat veškerá data profilu."</string>
- <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitorovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud je zadáno příliš mnoho nesprávných hesel, uzamknout telefon nebo vymazat veškerá data uživatele."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitoruje počet nesprávných hesel zadaných při odemykání obrazovky, a pokud je zadáno příliš mnoho nesprávných hesel, uzamkne telefon nebo vymaže veškerá data uživatele."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Změnit zámek obrazovky"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Změní se zámek obrazovky."</string>
<string name="policylab_forceLock" msgid="7360335502968476434">"Uzamknout obrazovku"</string>
<string name="policydesc_forceLock" msgid="1008844760853899693">"Určíte, jak a kdy se obrazovka uzamkne."</string>
- <string name="policylab_wipeData" msgid="1359485247727537311">"Vymazat všechna data"</string>
+ <string name="policylab_wipeData" msgid="1359485247727537311">"Mazat všechna data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Bez upozornění smazat všechna data tabletu obnovením továrních dat."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Provést obnovení továrních dat a bez upozornění tím vymazat data v zařízení Android TV."</string>
<string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Bez upozornění se smažou všechna data informačního a zábavního systému obnovením továrních dat."</string>
@@ -1727,9 +1727,9 @@
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"Chcete službě <xliff:g id="SERVICE">%1$s</xliff:g> povolit plnou kontrolu nad vaším zařízením?"</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"Plná kontrola je vhodná u aplikací, které vám pomáhají s usnadněním přístupu. U většiny aplikací však vhodná není."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Zobrazení a ovládání obrazovky"</string>
- <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Služba může číst veškerý obsah obrazovky a zobrazovat ho přes ostatní aplikace."</string>
+ <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Služba může číst veškerý obsah obrazovky a zobrazovat ho přes ostatní aplikace."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Zobrazení a provádění akcí"</string>
- <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Služba může sledovat vaše interakce s aplikací nebo hardwarovým senzorem a komunikovat s aplikacemi namísto vás."</string>
+ <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Služba může sledovat vaše interakce s aplikací nebo hardwarovým senzorem a komunikovat s aplikacemi za vás."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Povolit"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Zakázat"</string>
<string name="accessibility_dialog_button_uninstall" msgid="2952465517671708108">"Odinstalovat"</string>
@@ -1745,7 +1745,7 @@
<string name="color_inversion_feature_name" msgid="2672824491933264951">"Převrácení barev"</string>
<string name="color_correction_feature_name" msgid="7975133554160979214">"Korekce barev"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Režim jedné ruky"</string>
- <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Velmi tmavé"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Velmi tmavé zobrazení"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"Naslouchátka"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> byla vypnuta."</string>
@@ -1904,7 +1904,7 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Spořič baterie zapíná tmavý motiv a omezuje či vypíná aktivitu na pozadí, některé vizuální efekty, některé funkce a připojení k některým sítím."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"Spořič baterie zapíná tmavý motiv a omezuje či vypíná aktivitu na pozadí, některé vizuální efekty, některé funkce a připojení k některým sítím."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"S cílem snížit spotřebu dat brání spořič dat některým aplikacím odesílat nebo přijímat data na pozadí. Aplikace, kterou právě používáte, data přenášet může, ale může tak činit méně často. V důsledku toho se například obrázky nemusejí zobrazit, dokud na ně neklepnete."</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"S cílem snížit spotřebu dat brání spořič dat některým aplikacím odesílat nebo přijímat data na pozadí. Aplikace, kterou právě používáte, sice data využívat může, ale méně často. Může to například znamenat, že obrázky se zobrazí, až na ně klepnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Chcete zapnout Spořič dat?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Zapnout"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Na jednu minutu (do {formattedTime})}few{Na # minuty (do {formattedTime})}many{Na # minuty (do {formattedTime})}other{Na # minut (do {formattedTime})}}"</string>
@@ -2396,8 +2396,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Komunální"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikace je z bezpečnostních důvodů při sdílení obrazovky skryt"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automaticky připojeno k satelitu"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Zprávy můžete odesílat a přijímat bez mobilní sítě nebo sítě Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otevřít Zprávy"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 59be3dd..3f830c9 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Fælles"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Af sikkerhedsmæssige årsager vises appindhold ikke ved skærmdeling"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Der blev automatisk oprettet forbindelse til satellit"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Du kan sende og modtage beskeder uden et mobil- eller Wi-Fi-netværk"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Åbn Beskeder"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index b4882fe..59940a3 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -706,7 +706,7 @@
<string name="face_acquired_too_far" msgid="2922278214231064859">"Bewege das Smartphone näher heran"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Bewege das Smartphone nach oben"</string>
<string name="face_acquired_too_low" msgid="4075391872960840081">"Bewege das Smartphone nach unten"</string>
- <string name="face_acquired_too_right" msgid="6245286514593540859">"Bewege das Smartphone nach links"</string>
+ <string name="face_acquired_too_right" msgid="6245286514593540859">"Bewege das Smartphone nach links"</string>
<string name="face_acquired_too_left" msgid="9201762240918405486">"Bewege das Smartphone nach rechts"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Bitte sieh direkt auf dein Gerät."</string>
<string name="face_acquired_not_detected" msgid="1057966913397548150">"Gesicht nicht erkannt. Smartphone auf Augenhöhe halten."</string>
@@ -1929,12 +1929,9 @@
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Verwaltet von <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"An"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Aus"</string>
- <!-- no translation found for zen_mode_trigger_summary_divider_text (7461583466043698862) -->
- <skip />
- <!-- no translation found for zen_mode_trigger_summary_range_symbol_combination (1804900738798069619) -->
- <skip />
- <!-- no translation found for zen_mode_trigger_event_calendar_any (2086784607921121803) -->
- <skip />
+ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
+ <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
+ <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Alle Kalender"</string>
<string name="muted_by" msgid="91464083490094950">"Einige Töne werden von <xliff:g id="THIRD_PARTY">%1$s</xliff:g> stummgeschaltet"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Es liegt ein internes Problem mit deinem Gerät vor. Möglicherweise verhält es sich instabil, bis du es auf die Werkseinstellungen zurücksetzt."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Es liegt ein internes Problem mit deinem Gerät vor. Bitte wende dich diesbezüglich an den Hersteller."</string>
@@ -2397,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Gemeinsam genutzt"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App-Inhalte werden aus Sicherheitsgründen bei der Bildschirmfreigabe ausgeblendet"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automatisch mit Satellit verbunden"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Du kannst Nachrichten ohne Mobilfunknetz oder WLAN senden und empfangen"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages öffnen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 1b88a6a..cafccf5 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Δοκιμή"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Κοινόχρηστο"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Για λόγους ασφάλειας, έγινε απόκρυψη του περιεχομένου της εφαρμογής από την κοινή χρήση οθόνης"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Συνδέθηκε αυτόματα με δορυφόρο"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Μπορείτε να στέλνετε και να λαμβάνετε μηνύματα χωρίς δίκτυο κινητής τηλεφωνίας ή Wi-Fi."</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Άνοιγμα Messages"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 5a6c620..11fb50e 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Auto-connected to satellite"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"You can send and receive messages without a mobile or Wi-Fi network"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 4972e1b..60dbcd9 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Auto-connected to satellite"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"You can send and receive messages without a mobile or Wi-Fi network"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index c35c2ff..859d04a 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Auto-connected to satellite"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"You can send and receive messages without a mobile or Wi-Fi network"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 4f7f7a7..39e3d5da2 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1728,7 +1728,7 @@
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Ver y controlar la pantalla"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Puede leer todo el contenido en la pantalla y mostrar contenido sobre otras apps."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Ver y realizar acciones"</string>
- <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Puede realizar el seguimiento de tus interacciones con una app o un sensor de hardware, así como interactuar con las apps por ti."</string>
+ <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Puede seguir tus interacciones con una app o un sensor de hardware, así como interactuar con las apps por ti."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permitir"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Rechazar"</string>
<string name="accessibility_dialog_button_uninstall" msgid="2952465517671708108">"Desinstalar"</string>
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Probar"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Compartido"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Se ocultó el contenido de la app durante el uso compartido de la pantalla por motivos de seguridad"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Conexión automática a satélite"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Puedes enviar y recibir mensajes incluso si no tienes conexión a una red móvil o Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir Mensajes"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index bf9d526..d2aa32c 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -710,7 +710,7 @@
<string name="face_acquired_too_right" msgid="6245286514593540859">"Mueve el teléfono hacia la izquierda"</string>
<string name="face_acquired_too_left" msgid="9201762240918405486">"Mueve el teléfono hacia la derecha"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Mira de forma más directa al dispositivo."</string>
- <string name="face_acquired_not_detected" msgid="1057966913397548150">"No se detecta tu cara. Sujeta el teléfono a la altura de los ojos."</string>
+ <string name="face_acquired_not_detected" msgid="1057966913397548150">"No se puede detectar tu cara. Sujeta el teléfono a la altura de los ojos."</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"El teléfono se mueve demasiado. Mantenlo quieto."</string>
<string name="face_acquired_recalibrate" msgid="8724013080976469746">"Vuelve a registrar tu cara."</string>
<string name="face_acquired_too_different" msgid="4505278456634706967">"Cara no reconocida. Inténtalo de nuevo."</string>
@@ -1903,7 +1903,7 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales, ciertas funciones y algunas conexiones de red."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales, ciertas funciones y algunas conexiones de red."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"Para reducir el uso de datos, el ahorro de datos evita que algunas aplicaciones envíen o reciban datos en segundo plano. Si estás usando una aplicación, podrá acceder a datos, pero con menos frecuencia. Esto significa que es posible que, por ejemplo, algunas imágenes no se muestren hasta que las toques."</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"Para reducir el uso de datos, Ahorro de datos evita que algunas aplicaciones envíen o reciban datos en segundo plano. Si estás usando una aplicación, podrá acceder a datos, pero con menos frecuencia. Esto significa que es posible que, por ejemplo, algunas imágenes no se muestren hasta que las toques."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"¿Activar Ahorro de datos?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Durante un minuto (hasta las {formattedTime})}many{Durante # minutos (hasta las {formattedTime})}other{Durante # minutos (hasta las {formattedTime})}}"</string>
@@ -2032,7 +2032,7 @@
<string name="pin_specific_target" msgid="7824671240625957415">"Fijar <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="unpin_target" msgid="3963318576590204447">"No fijar"</string>
<string name="unpin_specific_target" msgid="3859828252160908146">"No fijar <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="app_info" msgid="6113278084877079851">"Información de la aplicación"</string>
+ <string name="app_info" msgid="6113278084877079851">"Información de la app"</string>
<string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="6577581216125805905">"Iniciando demostración…"</string>
<string name="demo_restarting_message" msgid="1160053183701746766">"Restableciendo dispositivo…"</string>
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Prueba"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Común"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenido de la aplicación oculto en pantalla compartida por motivos de seguridad"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Conectado automáticamente al satélite"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Puedes enviar y recibir mensajes sin una red móvil o Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abre Mensajes"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index cc82e47..99809d3 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Jagatud"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Rakenduse sisu on ekraani jagamises turvalisuse huvides peidetud"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Satelliidiga loodi automaatselt ühendus"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Teil on võimalik sõnumeid saata ja vastu võtta ilma mobiilside- ja WiFi-võrguta"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Ava rakendus Messages"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 34c0663..c8c2e45 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Probakoa"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Partekatua"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Aplikazioko edukia ezkutatu egin da pantaila partekatzeko eginbidetik, segurtasuna bermatzeko"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automatikoki konektatu da satelitera"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Mezuak bidal eta jaso ditzakezu sare mugikorrik edo wifi-sarerik gabe"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Ireki Mezuak"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 4f2484e..c59ac02 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"آزمایش"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"عمومی"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"بهدلایل امنیتی، محتوای برنامه از دید همرسانی صفحهنمایش پنهان شد"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"بهطور خودکار به ماهواره متصل شد"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"میتوانید بدون شبکه تلفن همراه یا Wi-Fi پیام ارسال و دریافت کنید"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"باز کردن «پیامها»"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 81b9848..7612906 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1384,7 +1384,7 @@
<string name="no_permissions" msgid="5729199278862516390">"Lupia ei tarvita"</string>
<string name="perm_costs_money" msgid="749054595022779685">"tämä voi maksaa"</string>
<string name="dlg_ok" msgid="5103447663504839312">"OK"</string>
- <string name="usb_charging_notification_title" msgid="1674124518282666955">"Laite lataa USB-yhteydellä"</string>
+ <string name="usb_charging_notification_title" msgid="1674124518282666955">"Laitetta ladataan USB:llä"</string>
<string name="usb_supplying_notification_title" msgid="5378546632408101811">"Ladataan yhdistettyä laitetta USB:n kautta"</string>
<string name="usb_mtp_notification_title" msgid="1065989144124499810">"USB-tiedostonsiirto on käytössä"</string>
<string name="usb_ptp_notification_title" msgid="5043437571863443281">"PTP USB:n kautta on käytössä"</string>
@@ -1655,7 +1655,7 @@
<string name="wireless_display_route_description" msgid="8297563323032966831">"Langaton näyttö"</string>
<string name="media_route_button_content_description" msgid="2299223698196869956">"Suoratoisto"</string>
<string name="media_route_chooser_title" msgid="6646594924991269208">"Yhdistä laitteeseen"</string>
- <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Lähetä näyttö laitteeseen"</string>
+ <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Striimaa näyttö laitteeseen"</string>
<string name="media_route_chooser_searching" msgid="6119673534251329535">"Etsitään laitteita…"</string>
<string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Asetukset"</string>
<string name="media_route_controller_disconnect" msgid="7362617572732576959">"Katkaise yhteys"</string>
@@ -1903,8 +1903,8 @@
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Virransäästö laittaa tumman teeman päälle ja rajoittaa tai laittaa pois päältä taustatoimintoja, tiettyjä ominaisuuksia sekä joitakin visuaalisia tehosteita ja verkkoyhteyksiä."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"Virransäästö laittaa tumman teeman päälle ja rajoittaa tai laittaa pois päältä taustatoimintoja, tiettyjä ominaisuuksia sekä joitakin visuaalisia tehosteita ja verkkoyhteyksiä."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Data Saver estää joitakin sovelluksia lähettämästä tai vastaanottamasta tietoja taustalla, jotta datan käyttöä voidaan vähentää. Käytössäsi oleva sovellus voi yhä käyttää dataa, mutta se saattaa tehdä niin tavallista harvemmin. Tämä voi tarkoittaa esimerkiksi sitä, että kuva ladataan vasta, kun kosketat sitä."</string>
- <string name="data_saver_enable_title" msgid="7080620065745260137">"Otetaanko Data Saver käyttöön?"</string>
- <string name="data_saver_enable_button" msgid="4399405762586419726">"Ota käyttöön"</string>
+ <string name="data_saver_enable_title" msgid="7080620065745260137">"Laitetaanko Data Saver päälle?"</string>
+ <string name="data_saver_enable_button" msgid="4399405762586419726">"Laita päälle"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Yhdeksi minuutiksi ({formattedTime} asti)}other{# minuutiksi ({formattedTime} asti)}}"</string>
<string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Yhdeksi minuutiksi ({formattedTime} asti)}other{# minuutiksi ({formattedTime} asti)}}"</string>
<string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Yhdeksi tunniksi ({formattedTime} asti)}other{# tunniksi ({formattedTime} asti)}}"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Testi"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Jaettu"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sovelluksen sisältö piilotettu näytön jakamiselta turvallisuussyistä"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Yhdistetty automaattisesti satelliittiin"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Voit lähettää ja vastaanottaa viestejä ilman mobiili‑ tai Wi-Fi-verkkoa"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Avaa Messages"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 15d36f5..7d86d83 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1930,12 +1930,9 @@
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Géré par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activé"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Désactivé"</string>
- <!-- no translation found for zen_mode_trigger_summary_divider_text (7461583466043698862) -->
- <skip />
- <!-- no translation found for zen_mode_trigger_summary_range_symbol_combination (1804900738798069619) -->
- <skip />
- <!-- no translation found for zen_mode_trigger_event_calendar_any (2086784607921121803) -->
- <skip />
+ <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
+ <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
+ <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"N\'importe quel agenda"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> désactive certains sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Un problème interne est survenu avec votre appareil. Il se peut qu\'il soit instable jusqu\'à ce que vous le réinitialisiez à ses paramètres par défaut."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Un problème interne est survenu avec votre appareil. Communiquez avec le fabricant pour obtenir plus de détails."</string>
@@ -2398,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'application est masqué du Partage d\'écran par mesure de sécurité"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Connecté au satellite automatiquement"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Vous pouvez envoyer et recevoir des messages sans avoir recours à un appareil mobile ou à un réseau Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Ouvrir Messages"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 0346822..80f1cf8 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -330,7 +330,7 @@
<string name="permgroupdesc_microphone" msgid="1047786732792487722">"enregistrer des fichiers audio"</string>
<string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Activité physique"</string>
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"accéder aux données d\'activité physique"</string>
- <string name="permgrouplab_camera" msgid="9090413408963547706">"Caméra"</string>
+ <string name="permgrouplab_camera" msgid="9090413408963547706">"Appareil photo"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"prendre des photos et enregistrer des vidéos"</string>
<string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Appareils à proximité"</string>
<string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"détecter des appareils à proximité et s\'y connecter"</string>
@@ -726,7 +726,7 @@
<skip />
<string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Impossible de créer votre empreinte faciale. Réessayez."</string>
<string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Lunettes sombres détectées. Votre visage doit être entièrement visible."</string>
- <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Visage partiellement couvert. Votre visage doit être entièrement visible."</string>
+ <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Masque détecté. Votre visage doit être entièrement visible."</string>
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Imposs. valider visage. Matériel non disponible."</string>
@@ -1059,7 +1059,7 @@
<string name="keyguard_accessibility_widget" msgid="6776892679715699875">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>"</string>
<string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"Sélecteur d\'utilisateur"</string>
<string name="keyguard_accessibility_status" msgid="6792745049712397237">"État"</string>
- <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"Caméra"</string>
+ <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"Appareil photo"</string>
<string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Commandes multimédias"</string>
<string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"Début de la réorganisation des widgets"</string>
<string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"Réorganisation des widgets terminée."</string>
@@ -2124,7 +2124,7 @@
<string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Fermer"</string>
<string name="notification_app_name_system" msgid="3045196791746735601">"Système"</string>
<string name="notification_app_name_settings" msgid="9088548800899952531">"Paramètres"</string>
- <string name="notification_appops_camera_active" msgid="8177643089272352083">"Caméra"</string>
+ <string name="notification_appops_camera_active" msgid="8177643089272352083">"Appareil photo"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Micro"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"se superpose aux autres applications sur l\'écran"</string>
<string name="notification_feedback_indicator" msgid="663476517711323016">"Envoyer des commentaires"</string>
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'appli est masqué lors du partage d\'écran pour des raisons de sécurité"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Connecté automatiquement au réseau satellite"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Vous pouvez envoyer et recevoir des messages sans connexion au réseau mobile ou Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Ouvrir Messages"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index fa48e96..0dbf369 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -995,7 +995,7 @@
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Téntao de novo"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Téntao de novo"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"Desbloquea para gozar todas as funcións e datos"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"Desbloquear para gozar de todas as funcións e datos"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"Superouse o número máximo de intentos de desbloqueo facial"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"Non hai SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"Non hai ningunha SIM na tableta."</string>
@@ -1970,7 +1970,7 @@
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sen clasificar"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Ti defines a importancia destas notificacións."</string>
<string name="importance_from_person" msgid="4235804979664465383">"É importante polas persoas involucradas."</string>
- <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificación de aplicacións personalizada"</string>
+ <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificación de aplicación personalizada"</string>
<string name="user_creation_account_exists" msgid="2239146360099708035">"Queres permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario novo con <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Xa existe un usuario con esta conta)"</string>
<string name="user_creation_adding" msgid="7305185499667958364">"Queres permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario novo con <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="supervised_user_creation_label" msgid="6884904353827427515">"Engadir usuario supervisado"</string>
@@ -2031,7 +2031,7 @@
<string name="pin_specific_target" msgid="7824671240625957415">"Fixar a <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="unpin_target" msgid="3963318576590204447">"Deixar de fixar"</string>
<string name="unpin_specific_target" msgid="3859828252160908146">"Deixar de fixar a <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="app_info" msgid="6113278084877079851">"Información da aplicación"</string>
+ <string name="app_info" msgid="6113278084877079851">"Información da app"</string>
<string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="6577581216125805905">"Iniciando demostración…"</string>
<string name="demo_restarting_message" msgid="1160053183701746766">"Restablecendo dispositivo…"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Proba"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Compartido"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Por motivos de seguranza, ocultouse o contido da aplicación para que no se mostre na pantalla compartida"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Conexión automática ao satélite"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Podes enviar e recibir mensaxes sen unha rede de telefonía móbil ou wifi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir Mensaxes"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index fb202ba..b3cf16b 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"પરીક્ષણ કરો"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"કૉમ્યુનલ"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"સુરક્ષા માટે સ્ક્રીન શેર કરતી વખતે ઍપનું કન્ટેન્ટ છુપાવેલું છે"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"સેટેલાઇટ સાથે ઑટોમૅટિક રીતે કનેક્ટેડ"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"તમે મોબાઇલ અથવા વાઇ-ફાઇ નેટવર્ક વિના મેસેજ મોકલી અને પ્રાપ્ત કરી શકો છો"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ખોલો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index fd99d19..c2b36b7 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -278,7 +278,7 @@
<string name="global_action_settings" msgid="4671878836947494217">"सेटिंग"</string>
<string name="global_action_assist" msgid="2517047220311505805">"सहायता"</string>
<string name="global_action_voice_assist" msgid="6655788068555086695">"आवाज़ से डिवाइस का इस्तेमाल"</string>
- <string name="global_action_lockdown" msgid="2475471405907902963">"फ़ाेन लॉक करें"</string>
+ <string name="global_action_lockdown" msgid="2475471405907902963">"लॉकडाउन"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
<string name="notification_hidden_text" msgid="2835519769868187223">"नई सूचना"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"सामान्य कीबोर्ड"</string>
@@ -452,7 +452,7 @@
<string name="permdesc_foregroundServiceSpecialUse" msgid="646713654541885919">"इससे ऐप्लिकेशन, \"specialUse\" टाइप वाली फ़ोरग्राउंड सेवाओं का इस्तेमाल कर पाता है"</string>
<string name="permlab_getPackageSize" msgid="375391550792886641">"पता करें कि ऐप मेमोरी में कितनी जगह है"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"ऐप को उसका कोड, डेटा, और कैश मेमोरी के आकारों को फिर से पाने देता है"</string>
- <string name="permlab_writeSettings" msgid="8057285063719277394">"सिस्टम सेटिंग बदलें"</string>
+ <string name="permlab_writeSettings" msgid="8057285063719277394">"सिस्टम की सेटिंग में बदलाव करे"</string>
<string name="permdesc_writeSettings" msgid="8293047411196067188">"ऐप्लिकेशन को सिस्टम सेटिंग डेटा में बदलाव करने देता है. नुकसान पहुंचाने वाले ऐप्लिकेशन आपके सिस्टम के कॉन्फ़िगरेशन को खराब सकते हैं."</string>
<string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"प्रारंभ होने पर चलाएं"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"ऐप्लिकेशन को सिस्टम से बूटिंग पूरी करते ही अपने आप शुरू करने देता है. इससे टैबलेट को शुरू होने में ज़्यादा समय लग सकता है और ऐप्लिकेशन निरंतर चलाकर संपूर्ण टैबलेट को धीमा करने देता है."</string>
@@ -831,7 +831,7 @@
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"स्क्रीन को अनलॉक करते समय गलत लिखे गए पासवर्ड की संख्या पर निगरानी करें, और बहुत ज़्यादा बार गलत पासवर्ड लिखे जाने पर टैबलेट लॉक करें या टैबलेट का संपूर्ण डेटा मिटाएं."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो अपने Android TV डिवाइस को तुरंत लॉक करें या इसका सभी डेटा मिटाएं."</string>
<string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो सूचना और मनोरंजन की सुविधा देने वाले डिवाइस को लॉक करें या इस डिवाइस का सारा डेटा मिटाएं."</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो अपने फ़ोन को तुरंत लॉक करें या फ़ोन का सारा डेटा मिटा दें."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"स्क्रीन को अनलॉक करते समय ध्यान रखेगा कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला जाएगा, तो फ़ोन को तुरंत लॉक करेगा या फ़ोन का सारा डेटा मिटा देगा."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"स्क्रीन का लॉक खोलते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें, और अगर बार-बार ज़्यादा पासवर्ड लिखे जाते हैं तो टैबलेट को लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटा दें."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो अपने Android TV डिवाइस को तुरंत लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटाएं."</string>
<string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो सूचना और मनोरंजन की सुविधा देने वाले डिवाइस को लॉक करें या इस प्रोफ़ाइल का सारा डेटा मिटाएं."</string>
@@ -844,7 +844,7 @@
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"फ़ैक्टरी डेटा रीसेट करके चेतावनी दिए बिना फ़ोन का डेटा मिटाना."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"फ़ैक्ट्री डेटा रीसेट करके अपने Android TV डिवाइस का डेटा बिना चेतावनी दिए मिटाएं."</string>
<string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"फ़ैक्ट्री डेटा रीसेट करके, बिना किसी चेतावनी के सूचना और मनोरंजन की सुविधा देने वाले डिवाइस में सेव डेटा को हमेशा के लिए मिटाएं."</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"इससे फ़ैक्टरी डेटा रीसेट करके, चेतावनी दिए बिना फ़ोन का डेटा मिट जाता है."</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"फ़ैक्टरी डेटा रीसेट करके, चेतावनी दिए बिना फ़ोन का डेटा मिटा देगा."</string>
<string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"प्रोफ़ाइल का डेटा मिटाना"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"उपयोगकर्ता डेटा मिटाएं"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"इस टैबलेट पर मौजूद इस उपयोगकर्ता का डेटा बिना चेतावनी के मिटा दें."</string>
@@ -1655,7 +1655,7 @@
<string name="wireless_display_route_description" msgid="8297563323032966831">"वायरलेस डिसप्ले"</string>
<string name="media_route_button_content_description" msgid="2299223698196869956">"कास्ट करें"</string>
<string name="media_route_chooser_title" msgid="6646594924991269208">"डिवाइस से कनेक्ट करें"</string>
- <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"स्क्रीन को डिवाइस में कास्ट करें"</string>
+ <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"स्क्रीन को डिवाइस पर कास्ट करें"</string>
<string name="media_route_chooser_searching" msgid="6119673534251329535">"डिवाइस खोजे जा रहे हैं…"</string>
<string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"सेटिंग"</string>
<string name="media_route_controller_disconnect" msgid="7362617572732576959">"डिसकनेक्ट करें"</string>
@@ -1722,14 +1722,14 @@
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"चालू न करें"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"चालू है"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"बंद है"</string>
- <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> को अपना डिवाइस पूरी तरह कंट्रोल करने की मंज़ूरी दें?"</string>
+ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> को अपना डिवाइस पूरी तरह कंट्रोल करने की अनुमति देनी है?"</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"पूरी तरह कंट्रोल करने की अनुमति उन ऐप्लिकेशन के लिए ठीक है जो सुलभता से जुड़ी ज़रूरतों के लिए बने हैं, लेकिन ज़्यादातर ऐप्लिकेशन के लिए यह ठीक नहीं है."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"स्क्रीन को देखें और कंट्रोल करें"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"यह स्क्रीन पर दिखने वाले कॉन्टेंट को पढ़ सकता है और उसे दूसरे ऐप्लिकेशन के ऊपर दिखा सकता है."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"देखें और कार्रवाई करें"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"यह आपके और किसी ऐप्लिकेशन या हार्डवेयर सेंसर के बीच होने वाले इंटरैक्शन को ट्रैक कर सकता है और आपकी तरफ़ से ऐप्लिकेशन के साथ इंटरैक्ट कर सकता है."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"अनुमति दें"</string>
- <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"इंकार करें"</string>
+ <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"न दें"</string>
<string name="accessibility_dialog_button_uninstall" msgid="2952465517671708108">"अनइंस्टॉल करें"</string>
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ऐप्लिकेशन की वजह से, अनुमति का अनुरोध समझने में परेशानी हो रही है. इसलिए, आपके जवाब की पुष्टि नहीं की जा सकी."</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"किसी सुविधा का इस्तेमाल करने के लिए, उस पर टैप करें:"</string>
@@ -1761,7 +1761,7 @@
<string name="owner_name" msgid="8713560351570795743">"मालिक"</string>
<string name="guest_name" msgid="8502103277839834324">"मेहमान"</string>
<string name="error_message_title" msgid="4082495589294631966">"गड़बड़ी"</string>
- <string name="error_message_change_not_allowed" msgid="843159705042381454">"आपका व्यवस्थापक इस बदलाव की अनुमति नहीं देता"</string>
+ <string name="error_message_change_not_allowed" msgid="843159705042381454">"आपका एडमिन इस बदलाव की अनुमति नहीं देता"</string>
<string name="app_not_found" msgid="3429506115332341800">"इस कार्यवाही को प्रबंधित करने के लिए कोई ऐप्स नहीं मिला"</string>
<string name="revoke" msgid="5526857743819590458">"रद्द करें"</string>
<string name="mediasize_iso_a0" msgid="7039061159929977973">"ISO A0"</string>
@@ -1902,7 +1902,7 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"ठीक है"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, इस मोड में बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और कुछ खास सुविधाएं कम या बंद हो जाती हैं. कुछ इंटरनेट कनेक्शन भी पूरी तरह काम नहीं करते."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, इस मोड में बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और कुछ सुविधाएं सीमित या बंद हो जाती हैं. कुछ इंटरनेट कनेक्शन भी पूरी तरह काम नहीं करते."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"डेटा खर्च को कम करने के लिए, डेटा बचाने की सेटिंग कुछ ऐप्लिकेशन को बैकग्राउंड में डेटा भेजने या डेटा पाने से रोकती है. फ़िलहाल, जिस ऐप्लिकेशन का इस्तेमाल किया जा रहा है वह डेटा ऐक्सेस कर सकता है, लेकिन ऐसा कभी-कभी ही हो पाएगा. उदाहरण के लिए, इमेज तब तक नहीं दिखेंगी, जब तक उन पर टैप नहीं किया जाएगा."</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"डेटा खर्च को कम करने के लिए, डेटा बचाने की सेटिंग बैकग्राउंड में चलने वाले कुछ ऐप्लिकेशन को डेटा भेजने या पाने से रोकती है. हालांकि, फ़िलहाल इस्तेमाल किया जा रहा ऐप्लिकेशन, डेटा को ऐक्सेस कर सकता है, लेकिन वह अक्सर ऐसा नहीं कर पाएगा. उदाहरण के लिए, ऐसा हो सकता है कि इमेज तब तक न दिखें, जब तक आप उन पर टैप न करें."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा बचाने की सेटिंग चालू करनी है?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"चालू करें"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{एक मिनट के लिए ({formattedTime} तक)}one{# मिनट के लिए ({formattedTime} तक)}other{# मिनट के लिए ({formattedTime} तक)}}"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"टेस्ट"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"कम्यूनिटी"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेयर करने के दौरान सुरक्षा के लिए, ऐप्लिकेशन का कॉन्टेंट छिपाया गया"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"सैटलाइट से अपने-आप कनेक्ट हो गया"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"मोबाइल या वाई-फ़ाई नेटवर्क के बिना भी मैसेज भेजे और पाए जा सकते हैं"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ऐप्लिकेशन खोलें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 5ac09ec..0243599 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1420,7 +1420,7 @@
<string name="share_remote_bugreport_action" msgid="7630880678785123682">"DIJELI"</string>
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ODBIJ"</string>
<string name="select_input_method" msgid="3971267998568587025">"Odabir načina unosa"</string>
- <string name="show_ime" msgid="6406112007347443383">"Zadrži na zaslonu dok je fizička tipkovnica aktivna"</string>
+ <string name="show_ime" msgid="6406112007347443383">"Zadržava se na zaslonu dok je fizička tipkovnica aktivna"</string>
<string name="hardware" msgid="3611039921284836033">"Upotreba zaslonske tipkovnice"</string>
<string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Konfigurirajte uređaj <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
<string name="select_multiple_keyboards_layout_notification_title" msgid="6999491025126641938">"Konfigurirajte fizičke tipkovnice"</string>
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije sakriven je od dijeljenja zaslona radi sigurnosti"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automatski povezano sa satelitom"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Možete slati i primati poruke bez mobilne mreže ili Wi-Fi mreže"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvori Poruke"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 06ca1b6..518e851 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1723,7 +1723,7 @@
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"BE"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"KI"</string>
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"Teljes körű vezérlést biztosít eszköze felett a(z) <xliff:g id="SERVICE">%1$s</xliff:g> szolgáltatás számára?"</string>
- <string name="accessibility_service_warning_description" msgid="291674995220940133">"A teljes vezérlés indokolt olyan alkalmazásoknál, amelyek kisegítő lehetőségeket nyújtanak, a legtöbb alkalmazásnál azonban nem."</string>
+ <string name="accessibility_service_warning_description" msgid="291674995220940133">"A teljes körű vezérlés indokolt olyan alkalmazásoknál, amelyek kisegítő lehetőségeket nyújtanak, a legtöbb alkalmazásnál azonban nem."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Képernyő megtekintése és kezelése"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Elolvashatja a képernyő tartalmát, és tartalmakat jeleníthet meg más alkalmazások felett."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Műveletek megtekintése és elvégzése"</string>
@@ -1894,7 +1894,7 @@
<string name="clone_profile_label_badge" msgid="1871997694718793964">"<xliff:g id="LABEL">%1$s</xliff:g> klónozása"</string>
<string name="private_profile_label_badge" msgid="1712086003787839183">"Privát <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"PIN-kód kérése a kitűzés feloldásához"</string>
- <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Feloldási minta kérése a rögzítés feloldásához"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Feloldási minta kérése a kitűzés feloldásához"</string>
<string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Jelszó kérése a rögzítés feloldásához"</string>
<string name="package_installed_device_owner" msgid="7035926868974878525">"A rendszergazda által telepítve"</string>
<string name="package_updated_device_owner" msgid="7560272363805506941">"A rendszergazda által frissítve"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Teszt"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Közös"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"A biztonság érdekében a képernyőmegosztástól elrejtett alkalmazástartalom"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automatikusan csatlakozva a műholdhoz"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Küldhet és fogadhat üzeneteket mobil- és Wi-Fi-hálózat nélkül is"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"A Messages megnyitása"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 89e087e..74d3751 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -647,17 +647,17 @@
<string name="biometric_error_generic" msgid="6784371929985434439">"Չհաջողվեց նույնականացնել"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Էկրանի կողպում"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Շարունակելու համար ապակողպեք էկրանը"</string>
- <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Մատը ուժեղ սեղմեք սկաների վրա"</string>
+ <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Մատը ուժեղ սեղմեք սկաներին"</string>
<string name="fingerprint_acquired_insufficient" msgid="2410176550915730974">"Մատնահետքը չի ճանաչվել։ Նորից փորձեք։"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Մաքրեք մատնահետքերի սկաները և նորից փորձեք"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Մաքրեք սկաները և նորից փորձեք"</string>
- <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Մատը ուժեղ սեղմեք սկաների վրա"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Մատը ուժեղ սեղմեք սկաներին"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Շատ դանդաղ անցկացրիք մատը: Փորձեք նորից:"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Փորձեք մեկ այլ մատնահետք"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Շատ լուսավոր է"</string>
<string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Հայտնաբերվել է սնուցման կոճակի սեղմում"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Փորձեք փոխել մատի դիրքը"</string>
- <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Թեթևակի փոխեք մատի դիրքն ամեն անգամ"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ամեն անգամ թեթևակի փոխեք մատի դիրքը"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_error_not_match" msgid="4599441812893438961">"Մատնահետքը չի ճանաչվել"</string>
@@ -704,8 +704,8 @@
<string name="face_acquired_too_dark" msgid="8539853432479385326">"Թույլ լուսավորություն"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Փոքր-ինչ հեռու պահեք հեռախոսը"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Մոտեցրեք հեռախոսը"</string>
- <string name="face_acquired_too_high" msgid="8278815780046368576">"Պահեք հեռախոսն ավելի վերև"</string>
- <string name="face_acquired_too_low" msgid="4075391872960840081">"Պահեք հեռախոսն ավելի ներքև"</string>
+ <string name="face_acquired_too_high" msgid="8278815780046368576">"Հեռախոսը շարժեք ավելի վերև"</string>
+ <string name="face_acquired_too_low" msgid="4075391872960840081">"Հեռախոսը շարժեք ավելի ներքև"</string>
<string name="face_acquired_too_right" msgid="6245286514593540859">"Հեռախոսը շարժեք դեպի ձախ"</string>
<string name="face_acquired_too_left" msgid="9201762240918405486">"Հեռախոսը շարժեք դեպի աջ"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Նայեք ուղիղ էկրանին։"</string>
@@ -1970,7 +1970,7 @@
<string name="default_notification_channel_label" msgid="3697928973567217330">"Չդասակարգված"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Դուք սահմանել եք այս ծանուցումների կարևորությունը:"</string>
<string name="importance_from_person" msgid="4235804979664465383">"Կարևոր է, քանի որ որոշակի մարդիկ են ներգրավված:"</string>
- <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Հավելվածի հատուկ ծանուցում"</string>
+ <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Հատուկ հավելվածի ծանուցում"</string>
<string name="user_creation_account_exists" msgid="2239146360099708035">"Թույլատրե՞լ <xliff:g id="APP">%1$s</xliff:g> հավելվածին <xliff:g id="ACCOUNT">%2$s</xliff:g> հաշվով նոր Օգտատեր ստեղծել (նման հաշվով Օգտատեր արդեն գոյություն ունի):"</string>
<string name="user_creation_adding" msgid="7305185499667958364">"Թույլատրե՞լ <xliff:g id="APP">%1$s</xliff:g> հավելվածին <xliff:g id="ACCOUNT">%2$s</xliff:g> հաշվով նոր Օգտատեր ստեղծել:"</string>
<string name="supervised_user_creation_label" msgid="6884904353827427515">"Ավելացնել վերահսկվող օգտատեր"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Փորձնական"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Ընդհանուր"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Անվտանգության նկատառումներից ելնելով՝ հավելվածի բովանդակությունը թաքցվել է էկրանի ցուցադրումից"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Ավտոմատ միացել է արբանյակին"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Դուք կարող եք ուղարկել և ստանալ հաղորդագրություններ՝ առանց բջջային կամ Wi-Fi կապի"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Բացել Messages-ը"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 616b30b..8a29efd 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -995,7 +995,7 @@
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Perbaiki!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Coba lagi"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Coba lagi"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"Membuka kunci untuk semua fitur dan data"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"Buka kunci untuk melihat semua fitur dan data"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"Percobaan Buka dengan Wajah melebihi batas maksimum"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"Tidak ada SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"Tidak ada SIM di tablet."</string>
@@ -1367,7 +1367,7 @@
<string name="sim_removed_message" msgid="8469588437451533845">"Jaringan seluler tidak akan tersedia sampai Anda memulai ulang dengan SIM yang valid."</string>
<string name="sim_done_button" msgid="6464250841528410598">"Selesai"</string>
<string name="sim_added_title" msgid="2976783426741012468">"SIM ditambahkan"</string>
- <string name="sim_added_message" msgid="6602906609509958680">"Mulai ulang perangkat Anda untuk mengakses jaringan selular."</string>
+ <string name="sim_added_message" msgid="6602906609509958680">"Mulai ulang perangkat Anda untuk mengakses jaringan seluler."</string>
<string name="sim_restart_button" msgid="8481803851341190038">"Mulai Ulang"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Aktifkan layanan seluler"</string>
<string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Download aplikasi operator untuk mengaktifkan SIM baru"</string>
@@ -1725,9 +1725,9 @@
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"Izinkan <xliff:g id="SERVICE">%1$s</xliff:g> mengontrol perangkat Anda secara penuh?"</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"Kontrol penuh sesuai untuk aplikasi yang mendukung kebutuhan aksesibilitas Anda, tetapi tidak untuk sebagian besar aplikasi."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Melihat dan mengontrol layar"</string>
- <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Voice Access dapat membaca semua konten di layar dan menampilkan konten di atas aplikasi lain."</string>
+ <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Membaca semua konten di layar dan menampilkan konten di atas aplikasi lain."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Menampilkan dan melakukan tindakan"</string>
- <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Voice Access dapat melacak interaksi Anda dengan aplikasi atau sensor hardware, dan berinteraksi dengan aplikasi untuk Anda."</string>
+ <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Melacak interaksi Anda dengan aplikasi atau sensor hardware, dan berinteraksi dengan aplikasi untuk Anda."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Izinkan"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Tolak"</string>
<string name="accessibility_dialog_button_uninstall" msgid="2952465517671708108">"Uninstal"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Pengujian"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Konten aplikasi disembunyikan dari berbagi layar untuk alasan keamanan"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Menghubungkan otomatis ke satelit"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Anda dapat mengirim dan menerima pesan tanpa jaringan seluler atau Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Buka Message"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 5d55ba8..caa801d 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -306,7 +306,7 @@
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Ýttu til að fá upplýsingar um rafhlöðu- og gagnanotkun"</string>
<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">"Örugg stilling"</string>
- <string name="android_system_label" msgid="5974767339591067210">"Android kerfið"</string>
+ <string name="android_system_label" msgid="5974767339591067210">"Android-kerfið"</string>
<string name="user_owner_label" msgid="8628726904184471211">"Skipta yfir í einkasnið"</string>
<string name="managed_profile_label" msgid="7316778766973512382">"Skipta yfir í vinnusnið"</string>
<string name="user_owner_app_label" msgid="1553595155465750298">"Skipta yfir í einkasnið <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -1723,9 +1723,9 @@
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"KVEIKT"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"SLÖKKT"</string>
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"Viltu leyfa „<xliff:g id="SERVICE">%1$s</xliff:g>“ að hafa fulla stjórn yfir tækinu þínu?"</string>
- <string name="accessibility_service_warning_description" msgid="291674995220940133">"Full stjórnun er viðeigandi fyrir forrit sem hjálpa þér ef þú hefur ekki aðgang, en ekki fyrir flest forrit."</string>
+ <string name="accessibility_service_warning_description" msgid="291674995220940133">"Full stjórn er viðeigandi fyrir forrit sem hjálpa þér ef þú hefur ekki aðgang, en ekki fyrir flest forrit."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Skoða og stjórna skjá"</string>
- <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Það getur lesið allt efni á skjánum og birt efni yfir öðrum forritum."</string>
+ <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Getur lesið allt efni á skjánum og birt efni yfir öðrum forritum."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Skoða og framkvæma aðgerðir"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Það getur fylgst með samskiptum þínum við forrit eða skynjara vélbúnaðar og haft samskipti við forrit fyrir þína hönd."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Leyfa"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Prófun"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Sameiginlegt"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Efni forrits falið í skjádeilingu af öryggisástæðum"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Tengdist sjálfkrafa við gervihnött"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Þú getur sent og móttekið skilaboð án tengingar við farsímakerfi eða Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Opna Messages"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 9d8d74a..a5373ab 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1193,7 +1193,7 @@
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Memoria insufficiente per il sistema. Assicurati di avere 250 MB di spazio libero e riavvia."</string>
<string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> è in esecuzione"</string>
<string name="app_running_notification_text" msgid="5120815883400228566">"Tocca per ulteriori informazioni o per interrompere l\'app."</string>
- <string name="ok" msgid="2646370155170753815">"OK"</string>
+ <string name="ok" msgid="2646370155170753815">"Ok"</string>
<string name="cancel" msgid="6908697720451760115">"Annulla"</string>
<string name="yes" msgid="9069828999585032361">"OK"</string>
<string name="no" msgid="5122037903299899715">"Annulla"</string>
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Condiviso"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenuti dell\'app nascosti dalla condivisione schermo per sicurezza"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Connessione automatica al satellite"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Puoi inviare e ricevere messaggi senza una rete mobile o Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Apri Messaggi"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index c256de9..e9e5585 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"בדיקה"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"שיתופי"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"תוכן האפליקציה מוסתר משיתוף המסך מטעמי אבטחה"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"חיבור אוטומטי ללוויין"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"אפשר לשלוח ולקבל הודעות ללא רשת סלולרית או רשת Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"לפתיחת Messages"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index eee2e3d..4eb0ced 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -196,7 +196,7 @@
<string name="work_profile_deleted" msgid="5891181538182009328">"仕事用プロファイルが削除されました"</string>
<string name="work_profile_deleted_details" msgid="3773706828364418016">"仕事用プロファイルの管理アプリがないか、破損しています。そのため仕事用プロファイルと関連データが削除されました。管理者にサポートをご依頼ください。"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"お使いの仕事用プロファイルはこのデバイスで使用できなくなりました"</string>
- <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"パスワード入力回数が上限を超えました"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"パスワード入力回数が上限に達しました"</string>
<string name="device_ownership_relinquished" msgid="4080886992183195724">"管理者により、デバイスの個人使用が許可されました"</string>
<string name="network_logging_notification_title" msgid="554983187553845004">"管理対象のデバイス"</string>
<string name="network_logging_notification_text" msgid="1327373071132562512">"このデバイスは組織によって管理され、ネットワーク トラフィックが監視される場合があります。詳しくはタップしてください。"</string>
@@ -671,8 +671,8 @@
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋の設定がタイムアウトしました。もう一度お試しください。"</string>
<string name="fingerprint_error_canceled" msgid="5541771463159727513">"指紋認証操作がキャンセルされました"</string>
<string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"指紋認証操作がユーザーによりキャンセルされました"</string>
- <string name="fingerprint_error_lockout" msgid="6626753679019351368">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"試行回数が上限に達しました。代わりに画面ロックを使用してください。"</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"試行回数が上限に達しました。代わりに画面ロックを使用してください。"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"指紋を処理できません。もう一度お試しください。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"指紋が登録されていません"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"このデバイスには指紋認証センサーがありません"</string>
@@ -734,8 +734,8 @@
<string name="face_error_canceled" msgid="2164434737103802131">"顔の操作をキャンセルしました。"</string>
<string name="face_error_user_canceled" msgid="5766472033202928373">"顔認証はユーザーによりキャンセルされました"</string>
<string name="face_error_lockout" msgid="7864408714994529437">"試行回数の上限です。後でもう一度お試しください。"</string>
- <string name="face_error_lockout_permanent" msgid="8533257333130473422">"試行回数が上限を超えました。顔認証を利用できません。"</string>
- <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"試行回数が上限を超えました。代わりに画面ロック解除を入力してください。"</string>
+ <string name="face_error_lockout_permanent" msgid="8533257333130473422">"試行回数が上限に達しました。顔認証を利用できません。"</string>
+ <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"試行回数が上限に達しました。代わりに画面ロック解除を入力してください。"</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"顔を確認できません。もう一度お試しください。"</string>
<string name="face_error_not_enrolled" msgid="1134739108536328412">"顔認証を設定していません"</string>
<string name="face_error_hw_not_present" msgid="7940978724978763011">"このデバイスは顔認証に対応していません"</string>
@@ -828,14 +828,14 @@
<string name="policylab_limitPassword" msgid="4851829918814422199">"パスワードルールの設定"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"画面ロックのパスワードとPINの長さと使用できる文字を制御します。"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"画面ロック解除試行の監視"</string>
- <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"画面のロック解除に正しくないパスワードを入力した回数を監視し、回数が多すぎる場合はタブレットをロックするかタブレットのデータをすべて消去します。"</string>
- <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"画面のロック解除の際に入力したパスワードが間違っていた回数を監視し、回数が多すぎる場合は Android TV デバイスをロックするか Android TV デバイスのデータをすべて消去します。"</string>
- <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"画面のロック解除に正しくないパスワードを入力した回数を監視し、回数が多すぎる場合はインフォテインメント システムをロックするかインフォテインメント システムのデータをすべて消去します。"</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"画面のロック解除に正しくないパスワードを入力した回数を監視し、回数が多すぎる場合はモバイルデバイスをロックするかモバイルデバイスのデータをすべて消去します。"</string>
- <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"画面のロック解除の際に入力したパスワードが間違っていた回数を監視し、回数が多すぎる場合はタブレットをロックするかこのユーザーのデータをすべて消去します。"</string>
- <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"画面のロック解除の際に入力したパスワードが間違っていた回数を監視し、回数が多すぎる場合は Android TV デバイスをロックするかこのユーザーのデータをすべて消去します。"</string>
- <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"画面のロック解除に正しくないパスワードを入力した回数を監視し、回数が多すぎる場合はインフォテインメント システムをロックするかこのプロファイルのデータをすべて消去します。"</string>
- <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"画面のロック解除の際に入力したパスワードが間違っていた回数を監視し、回数が多すぎる場合はスマートフォンをロックするかこのユーザーのデータをすべて消去します。"</string>
+ <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"画面のロック解除に失敗した回数を監視し、多すぎる場合はタブレットをロックするかタブレットのデータをすべて消去します。"</string>
+ <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"画面のロック解除に失敗した回数を監視し、多すぎる場合は Android TV デバイスをロックするか Android TV デバイスのデータをすべて消去します。"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"画面のロック解除に失敗した回数を監視し、多すぎる場合はインフォテインメント システムをロックするかインフォテインメント システムのデータをすべて消去します。"</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"画面のロック解除に失敗した回数を監視し、多すぎる場合はモバイルデバイスをロックするかモバイルデバイスのデータをすべて消去します。"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"画面のロック解除に失敗した回数を監視し、多すぎる場合はタブレットをロックするかこのユーザーのデータをすべて消去します。"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"画面のロック解除に失敗した回数を監視し、多すぎる場合は Android TV デバイスをロックするかこのユーザーのデータをすべて消去します。"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"画面のロック解除に失敗した回数を監視し、多すぎる場合はインフォテインメント システムをロックするかこのプロファイルのデータをすべて消去します。"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"画面のロック解除に失敗した回数を監視し、多すぎる場合はスマートフォンをロックするかこのユーザーのデータをすべて消去します。"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"画面ロックの変更"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"画面ロックを変更します。"</string>
<string name="policylab_forceLock" msgid="7360335502968476434">"画面のロック"</string>
@@ -1420,7 +1420,7 @@
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"共有しない"</string>
<string name="select_input_method" msgid="3971267998568587025">"入力方法の選択"</string>
<string name="show_ime" msgid="6406112007347443383">"物理キーボードが有効になっていても画面に表示させます"</string>
- <string name="hardware" msgid="3611039921284836033">"画面キーボードの使用"</string>
+ <string name="hardware" msgid="3611039921284836033">"画面キーボードを使用"</string>
<string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g>の設定"</string>
<string name="select_multiple_keyboards_layout_notification_title" msgid="6999491025126641938">"物理キーボードの設定"</string>
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"タップして言語とレイアウトを選択してください"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"テスト"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"共用"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"セキュリティ上、画面共有ではアプリの内容は非表示となります"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"衛星に自動接続しました"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"モバイル ネットワークや Wi-Fi ネットワークを使わずにメッセージを送受信できます"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"メッセージ アプリを開く"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index ececd92..d4b4d12 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"სატესტო"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"საერთო"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ეკრანის გაზიარებიდან აპის კონტენტი დამალულია უსაფრთხოების მიზნით"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"სატელიტთან ავტომატურად დაკავშირებულია"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"შეგიძლიათ გაგზავნოთ და მიიღოთ შეტყობინებები მობილური ან Wi-Fi ქსელის გარეშე"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages-ის გახსნა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 38c6f77..d1da74e 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -831,7 +831,7 @@
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Экран бекітпесін ашқан кезде терілген қате құпия сөздердің санын бақылау және планшетті бекіту немесе тым көп қате құпия сөздер терілген болса, планшеттің бүкіл деректерін өшіру."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Экранның құлпын ашу кезінде қате енгізілген құпия сөздердің санын бақылау, құпия сөз тым көп қате енгізілген жағдайда, Android TV құрылғысын құлыптау және Android TV құрылғыңыздың барлық деректерінен тазарту."</string>
<string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Экран құлпын ашқан кезде, терілген қате құпия сөздердің саны бақыланады, сондай-ақ құпия сөздер бірнеше рет қате терілсе, ақпараттық-сауықтық жүйе құлыпталады немесе оның барлық дерегі жойылады."</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Экран бекітпесін ашқан кезде терілген қате құпия сөздердің санын бақылау және телефонды бекіту немесе тым көп қате құпия сөздер терілген болса, телефонның бүкіл деректерін өшіру."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Экран құлпын ашқан кезде терілген қате құпия сөздердің санын бақылау және құпия сөз тым көп рет қате терілгенде, телефонды құлыптау немесе оның бүкіл деректерін өшіру."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Экран бекітпесін ашқанда терілген қате құпия сөздердің санын бақылау және тым көп қате құпия сөздер терілсе, планшетті бекіту немесе осы пайдаланушының барлық деректерін өшіру."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Экранның құлпын ашу кезінде қате енгізілген құпия сөздердің санын бақылау, құпия сөз тым көп қате енгізілген жағдайда, Android TV құрылғысын құлыптау және барлық пайдаланушы деректерінен тазарту."</string>
<string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Экран құлпын ашқан кезде, терілген қате құпия сөздердің саны бақыланады, сондай-ақ құпия сөздер бірнеше рет қате терілсе, ақпараттық-сауықтық жүйе құлыпталады немесе осы профильдің барлық дерегі жойылады."</string>
@@ -995,7 +995,7 @@
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Дұрыс!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Қайталап көріңіз"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Қайталап көріңіз"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"Мүмкіндіктер мен деректер үшін құлыпты ашыңыз"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"Барлық функция мен дерек үшін құлыпты ашыңыз"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"Бет тану арқылы ашу әрекеттері анықталған шегінен асып кетті"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"SIM картасы жоқ."</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"Планшетте SIM картасы жоқ."</string>
@@ -1970,7 +1970,7 @@
<string name="default_notification_channel_label" msgid="3697928973567217330">"Санатқа жатқызылмаған"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Сіз осы хабарландырулардың маңыздылығын орнатасыз."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Қатысты адамдарға байланысты бұл маңызды."</string>
- <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Арнаулы хабар хабарландыруы"</string>
+ <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Арнаулы қолданба хабарландыруы"</string>
<string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="APP">%1$s</xliff:g> қолданбасына <xliff:g id="ACCOUNT">%2$s</xliff:g> аккаунты бар жаңа пайдаланушы (мұндай аккаунтқа ие пайдаланушы бұрыннан бар) жасауға рұқсат етілсін бе?"</string>
<string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="APP">%1$s</xliff:g> қолданбасына <xliff:g id="ACCOUNT">%2$s</xliff:g> аккаунты бар жаңа пайдаланушы жасауға рұқсат етілсін бе?"</string>
<string name="supervised_user_creation_label" msgid="6884904353827427515">"Бақыланатын пайдаланушыны қосу"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Сынақ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Қауіпсіздік мақсатында қолданба контенті экранды көрсету кезінде жасырылды."</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Жерсерік қызметіне автоматты түрде қосылды"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Мобильдік не Wi-Fi желісіне қосылмастан хабар жібере аласыз және ала аласыз."</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages қолданбасын ашу"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 6c0a195..59a6e30 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -828,14 +828,14 @@
<string name="policylab_limitPassword" msgid="4851829918814422199">"កំណត់ក្បួនពាក្យសម្ងាត់"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"គ្រប់គ្រងប្រវែង និងតួអក្សរដែលអនុញ្ញាតឲ្យប្រើក្នុងពាក្យសម្ងាត់ និងលេខសម្ងាត់ចាក់សោអេក្រង់។"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"តាមដានការព្យាយាមដោះសោអេក្រង់"</string>
- <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ពិនិត្យចំនួនបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ។ ពេលដោះសោអេក្រង់ និងចាក់សោទូរស័ព្ទ ឬលុបទិន្នន័យទូរស័ព្ទទាំងអស់ ប្រសិនបើមានពាក្យសម្ងាត់បញ្ចូលមិនត្រឹមត្រូវច្រើនដងពេក។"</string>
+ <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ពិនិត្យមើលចំនួនដងនៃការវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោថេប្លេត ឬលុបទិន្នន័យថេប្លេតទាំងអស់ ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ពិនិត្យចំនួននៃការវាយបញ្ចូលពាក្យសម្ងាត់ដែលមិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោឧបករណ៍ Android TV របស់អ្នក ឬលុបទិន្នន័យឧបករណ៍ Android TV របស់អ្នកទាំងអស់ ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់ខុសច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ពិនិត្យមើលចំនួនដងនៃការវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោប្រព័ន្ធព័ត៌មាននិងកម្សាន្ត ឬលុបទិន្នន័យទាំងអស់របស់ប្រព័ន្ធព័ត៌មាននិងកម្សាន្ត ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវច្រើនដងពេក។"</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ពិនិត្យចំនួនបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ។ ពេលដោះសោអេក្រង់ និងចាក់សោទូរស័ព្ទ ឬលុបទិន្នន័យទូរស័ព្ទទាំងអស់ ប្រសិនបើមានពាក្យសម្ងាត់បញ្ចូលមិនត្រឹមត្រូវច្រើនដងពេក។"</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ពិនិត្យមើលចំនួនដងនៃការវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោទូរសព្ទ ឬលុបទិន្នន័យទូរសព្ទទាំងអស់ ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ត្រួតពិនិត្យចំនួននៃការវាយបញ្ចូលពាក្យសម្ងាត់ដែលមិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោថេប្លេត ឬលុបទិន្នន័យអ្នកប្រើនេះទាំងអស់ ប្រសិនបើមានការវាយបញ្ចូលពាក្យសម្ងាត់ខុសច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ពិនិត្យចំនួននៃការវាយបញ្ចូលពាក្យសម្ងាត់ដែលមិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោឧបករណ៍ Android TV របស់អ្នក ឬលុបទិន្នន័យរបស់អ្នកប្រើប្រាស់នេះទាំងអស់ ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់ខុសច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ពិនិត្យមើលចំនួនដងនៃការវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោប្រព័ន្ធព័ត៌មាននិងកម្សាន្ត ឬលុបទិន្នន័យទាំងអស់របស់កម្រងព័ត៌មាននេះ ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវច្រើនដងពេក។"</string>
- <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ត្រួតពិនិត្យចំនួននៃការវាយបញ្ចូលពាក្យសម្ងាត់ដែលមិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោទូរស័ព្ទ ឬលុបទិន្នន័យអ្នកប្រើនេះទាំងអស់ ប្រសិនបើមានការវាយបញ្ចូលពាក្យសម្ងាត់ខុសច្រើនដងពេក។"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ពិនិត្យមើលចំនួនដងនៃការវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោទូរសព្ទ ឬលុបទិន្នន័យទូរសព្ទទាំងអស់ ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវច្រើនដងពេក។"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ប្តូរការចាក់សោអេក្រង់"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ប្តូរការចាក់សោអេក្រង់។"</string>
<string name="policylab_forceLock" msgid="7360335502968476434">"ចាក់សោអេក្រង់"</string>
@@ -995,7 +995,7 @@
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ត្រឹមត្រូវ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ព្យាយាមម្ដងទៀត"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"ព្យាយាមម្ដងទៀត"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"ដោះសោលក្ខណៈពិសេស និងទិន្នន័យទាំងអស់"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"ដោះសោមុខងារ និងទិន្នន័យទាំងអស់"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"បានលើសការព្យាយាមដោះសោតាមទម្រង់មុខ"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"គ្មានស៊ីមទេ"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"គ្មានស៊ីមក្នុងថេប្លេតទេ។"</string>
@@ -1761,7 +1761,7 @@
<string name="owner_name" msgid="8713560351570795743">"ម្ចាស់"</string>
<string name="guest_name" msgid="8502103277839834324">"ភ្ញៀវ"</string>
<string name="error_message_title" msgid="4082495589294631966">"កំហុស"</string>
- <string name="error_message_change_not_allowed" msgid="843159705042381454">"ការផ្លាស់ប្ដូរនេះមិនត្រូវបានអនុញ្ញាតដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
+ <string name="error_message_change_not_allowed" msgid="843159705042381454">"ការផ្លាស់ប្ដូរនេះមិនត្រូវបានអនុញ្ញាតដោយអ្នកគ្រប់គ្រងរបស់អ្នកទេ"</string>
<string name="app_not_found" msgid="3429506115332341800">"រកមិនឃើញកម្មវិធី ដើម្បីគ្រប់គ្រងសកម្មភាពនេះ"</string>
<string name="revoke" msgid="5526857743819590458">"ដកហូត"</string>
<string name="mediasize_iso_a0" msgid="7039061159929977973">"ISO A0"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"ការធ្វើតេស្ត"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ទូទៅ"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"បានលាក់ខ្លឹមសារកម្មវិធីពីការបង្ហាញអេក្រង់ដើម្បីសុវត្ថិភាព"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"ភ្ជាប់ដោយស្វ័យប្រវត្តិទៅផ្កាយរណប"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"អ្នកអាចផ្ញើ និងទទួលសារដោយមិនប្រើបណ្តាញទូរសព្ទចល័ត ឬ Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"បើកកម្មវិធី Messages"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index faa46ba..b1a7472 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1419,7 +1419,7 @@
<string name="share_remote_bugreport_action" msgid="7630880678785123682">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ನಿರಾಕರಿಸಿ"</string>
<string name="select_input_method" msgid="3971267998568587025">"ಇನ್ಪುಟ್ ವಿಧಾನವನ್ನು ಆರಿಸಿ"</string>
- <string name="show_ime" msgid="6406112007347443383">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್ ಸಕ್ರಿಯವಾಗಿರುವಾಗ ಅದನ್ನು ಪರದೆಯ ಮೇಲಿರಿಸಿ"</string>
+ <string name="show_ime" msgid="6406112007347443383">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್ ಸಕ್ರಿಯವಾಗಿರುವಾಗ ಅದನ್ನು ಸ್ಕ್ರೀನ್ ಮೇಲಿರಿಸಿ"</string>
<string name="hardware" msgid="3611039921284836033">"ಆನ್-ಸ್ಕ್ರೀನ್ ಕೀಬೋರ್ಡ್ ಬಳಸಿ"</string>
<string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> ಕಾನ್ಫಿಗರ್ ಮಾಡಿ"</string>
<string name="select_multiple_keyboards_layout_notification_title" msgid="6999491025126641938">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್ಗಳನ್ನು ಕಾನ್ಫಿಗರ್ ಮಾಡಿ"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"ಪರೀಕ್ಷೆ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ಸಮುದಾಯ"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ಭದ್ರತೆಗಾಗಿ ಸ್ಕ್ರೀನ್ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯಲ್ಲಿ ಆ್ಯಪ್ ಕಂಟೆಂಟ್ ಅನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"ಸ್ಯಾಟಲೈಟ್ಗೆ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"ನೀವು ಮೊಬೈಲ್ ಅಥವಾ ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ ಇಲ್ಲದೆಯೇ ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಬಹುದು ಮತ್ತು ಸ್ವೀಕರಿಸಬಹುದು"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ಅನ್ನು ತೆರೆಯಿರಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 9eaa414..baa2604 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"테스트"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"공동"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"보안을 위해 화면 공유에서 앱 콘텐츠가 숨겨집니다."</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"위성에 자동 연결됨"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"모바일 또는 Wi-Fi 네트워크 없이 메시지를 주고 받을 수 있습니다"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"메시지 열기"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 9efccff..f02b58c 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -995,7 +995,7 @@
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Туура!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Дагы аракет кылыңыз"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Дагы аракет кылыңыз"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"Элементтердин жана дайындардын кулпусун ачуу"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"Функциялар менен колдонмолордун кулпусун ачуу"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"Жүзүнөн таанып ачуу аракеттеринин чегинен аштыңыз"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"SIM карта жок"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"Планшетте SIM карта жок."</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Сыноо"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Коопсуздук үчүн колдонмодогу контент бөлүшүлгөн экрандан жашырылды"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Спутникке автоматтык түрдө туташтырылган"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Сиз мобилдик же Wi-Fi тармагы жок эле билдирүүлөрдү жөнөтүп, ала аласыз"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Жазышуулар колдонмосун ачуу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 752e68e..e94f2d3 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1877,7 +1877,7 @@
<string name="restr_pin_try_later" msgid="5897719962541636727">"ລອງໃໝ່ອີກຄັ້ງໃນພາຍຫລັງ."</string>
<string name="immersive_cling_title" msgid="2307034298721541791">"ການເບິ່ງເຕັມໜ້າຈໍ"</string>
<string name="immersive_cling_description" msgid="7092737175345204832">"ຫາກຕ້ອງການອອກ, ໃຫ້ຮູດຈາກທາງເທິງລົງມາທາງລຸ່ມ."</string>
- <string name="immersive_cling_positive" msgid="7047498036346489883">"ໄດ້ແລ້ວ"</string>
+ <string name="immersive_cling_positive" msgid="7047498036346489883">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ໝຸນເພື່ອມຸມມອງທີ່ດີຂຶ້ນ"</string>
<string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ເປີດ <xliff:g id="NAME">%s</xliff:g> ໃນໂໝດເຕັມຈໍເພື່ອມຸມມອງທີ່ດີຂຶ້ນ"</string>
<string name="done_label" msgid="7283767013231718521">"ແລ້ວໆ"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"ທົດສອບ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ສ່ວນກາງ"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ເນື້ອຫາແອັບຖືກເຊື່ອງໄວ້ຈາກການແບ່ງປັນໜ້າຈໍເພື່ອຄວາມປອດໄພ"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"ເຊື່ອມຕໍ່ກັບດາວທຽມໂດຍອັດຕະໂນມັດ"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"ທ່ານສາມາດສົ່ງ ແລະ ຮັບຂໍ້ຄວາມໂດຍບໍ່ຕ້ອງໃຊ້ເຄືອຂ່າຍມືຖື ຫຼື Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"ເປີດ Messages"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 2fd6ded..da46662 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -2396,8 +2396,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Bandymas"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Bendruomenės"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Programos turinys paslėptas bendrinant ekraną saugumo sumetimais"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automatiškai prisijungta prie palydovinio ryšio"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Galite siųsti ir gauti pranešimus be mobiliojo ryšio ar „Wi-Fi“ tinklo"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Atidaryti programą „Messages“"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 066cd4b..9fcf2d1 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Testēšanai"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Kopīgs"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Drošības nolūkos lietotnes saturs kopīgotajā ekrānā ir paslēpts"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automātiski izveidots savienojums ar satelītu"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Varat sūtīt un saņemt ziņojumus bez mobilā vai Wi-Fi tīkla."</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Atvērt lietotni Ziņojumi"</string>
diff --git a/core/res/res/values-mcc404/config.xml b/core/res/res/values-mcc404/config.xml
index 4cadef7..0cb1029 100644
--- a/core/res/res/values-mcc404/config.xml
+++ b/core/res/res/values-mcc404/config.xml
@@ -18,8 +18,6 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Whether camera shutter sound is forced or not (country specific). -->
- <bool name="config_camera_sound_forced">true</bool>
<!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
<bool name="config_showAreaUpdateInfoSettings">true</bool>
</resources>
diff --git a/core/res/res/values-mcc405/config.xml b/core/res/res/values-mcc405/config.xml
index 4cadef7..0cb1029 100644
--- a/core/res/res/values-mcc405/config.xml
+++ b/core/res/res/values-mcc405/config.xml
@@ -18,8 +18,6 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Whether camera shutter sound is forced or not (country specific). -->
- <bool name="config_camera_sound_forced">true</bool>
<!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
<bool name="config_showAreaUpdateInfoSettings">true</bool>
</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index a8d5ea4..9b2b6ab 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -995,7 +995,7 @@
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Точно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Обидете се повторно"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Обидете се повторно"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"Отклучи за сите функции и податоци"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"Отклучете за пристап до сите функции и податоци"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"Максималниот број обиди на отклучување со лик е надминат"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"Нема SIM-картичка"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"Нема SIM-картичка во таблетот."</string>
@@ -1105,7 +1105,7 @@
<string name="menu_sym_shortcut_label" msgid="4037566049061218776">"копче Sym+"</string>
<string name="menu_function_shortcut_label" msgid="2367112760987662566">"копче Function+"</string>
<string name="menu_space_shortcut_label" msgid="5949311515646872071">"празен простор"</string>
- <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"внеси"</string>
+ <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"enter"</string>
<string name="menu_delete_shortcut_label" msgid="4365787714477739080">"избриши"</string>
<string name="search_go" msgid="2141477624421347086">"Пребарај"</string>
<string name="search_hint" msgid="455364685740251925">"Пребарување…"</string>
@@ -1674,7 +1674,7 @@
<string name="kg_wrong_password" msgid="2384677900494439426">"Погрешна лозинка"</string>
<string name="kg_wrong_pin" msgid="3680925703673166482">"Погрешен PIN"</string>
<string name="kg_pattern_instructions" msgid="8366024510502517748">"Употреби ја својата шема"</string>
- <string name="kg_sim_pin_instructions" msgid="6479401489471690359">"Внеси PIN на SIM картичка"</string>
+ <string name="kg_sim_pin_instructions" msgid="6479401489471690359">"Внесете PIN за SIM-картичката"</string>
<string name="kg_pin_instructions" msgid="7355933174673539021">"Впишете PIN"</string>
<string name="kg_password_instructions" msgid="7179782578809398050">"Внеси лозинка"</string>
<string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"SIM картичката е сега оневозможена. Внесете ПУК код за да продолжите. Контактирајте го операторот за детали."</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Профил за тестирање"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Профил на заедницата"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Од безбедносни причини, содржините на апликацијата се скриени од споделувањето екран"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Поврзано со сателит автоматски"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Може да испраќате и примате пораки без мобилна или Wi-Fi мрежа"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Отворете ја Messages"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 32f2daf..44af8b6 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -831,7 +831,7 @@
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുക, വളരെയധികം തെറ്റായ പാസ്വ്ഡുകൾ ടൈപ്പുചെയ്തിട്ടുണ്ടെങ്കിൽ ടാബ്ലെറ്റ് ലോക്കുചെയ്യുകയോ ടാബ്ലെറ്റിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്തിട്ടുണ്ടെങ്കിൽ നിങ്ങളുടെ Android TV ലോക്ക് ചെയ്യുകയോ Android TV-യിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുക. നിരവധി തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്താൽ, ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റം ലോക്ക് ചെയ്യുകയോ ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റത്തിന്റെ ഡാറ്റ മുഴുവനും മായ്ക്കുകയോ ചെയ്യുക."</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുക, വളരെയധികം തെറ്റായ പാസ്വ്ഡുകൾ ടൈപ്പുചെയ്തിട്ടുണ്ടെങ്കിൽ ഫോൺ ലോക്കുചെയ്യുകയോ ഫോണിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോചെയ്യുക."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുക, വളരെയധികം തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പുചെയ്തിട്ടുണ്ടെങ്കിൽ ഫോൺ ലോക്കുചെയ്യുകയോ ഫോണിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോചെയ്യുക."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ പാസ്വേഡ് ടൈപ്പുചെയ്തെങ്കിൽ ടാബ്ലെറ്റ് ലോക്കുചെയ്യുകയോ ഈ എല്ലാ ഉപയോക്തൃവിവരവും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്തിട്ടുണ്ടെങ്കിൽ നിങ്ങളുടെ Android TV ലോക്ക് ചെയ്യുകയോ ഈ ഉപയോക്തൃ ഡാറ്റയെല്ലാം മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിച്ച്, നിരവധി തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്താൽ ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റം ലോക്ക് ചെയ്യുകയോ ഈ പ്രൊഫൈലിന്റെ ഡാറ്റ മുഴുവനും മായ്ക്കുകയോ ചെയ്യുക."</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"ടെസ്റ്റ്"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"കമ്മ്യൂണൽ"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ആപ്പ് ഉള്ളടക്കം, അതിന്റെ സുരക്ഷയ്ക്കായി സ്ക്രീൻ പങ്കിടലിൽ നിന്ന് മറച്ചിരിക്കുന്നു"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"സാറ്റലൈറ്റിലേക്ക് സ്വയമേവ കണക്റ്റ് ചെയ്തു"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"മൊബൈലോ വൈഫൈ നെറ്റ്വർക്കോ ഇല്ലാതെ തന്നെ സന്ദേശങ്ങൾ അയയ്ക്കാനും സ്വീകരിക്കാനും നിങ്ങൾക്ക് കഴിയും"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages തുറക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index a926e60..beda8ee 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Туршилт"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Нийтийн"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Аюулгүй байдлын улмаас аппын контентыг дэлгэц хуваалцахаас нуусан"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Хиймэл дагуулд автоматаар холбогдсон"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Та мобайл эсвэл Wi-Fi сүлжээгүйгээр мессеж илгээх болон хүлээн авах боломжтой"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Мессежийг нээх"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index c10b741..9d72fcd 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -615,7 +615,7 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"स्क्रीन लॉक क्लिष्टतेची विनंती करा"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"अॅपला स्क्रीन लॉक क्लिष्टता पातळी (उच्च, मध्यम, खालची किंवा काहीही नाही) जाणून घेऊ देते, जी लांबीची संभाव्य रेंज आणि स्क्रीन लॉकचा प्रकार सूचित करते. अॅप वापरकर्त्यांना असेदेखील सुचवू शकते की त्यांनी स्क्रीन लॉक ठरावीक पातळीपर्यंत अपडेट करावे, परंतु वापरकर्ते त्याकडे मोकळेपणाने दुर्लक्ष करू शकतात आणि तेथून नेव्हिगेट करू शकतात. स्क्रीन लॉक प्लेनटेक्स्टमध्ये स्टोअर केले जात नसल्यामुळे अॅपला नेमका पासवर्ड माहीत नसतो याची नोंद घ्या."</string>
- <string name="permlab_postNotification" msgid="4875401198597803658">"सूचना दाखवा"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"नोटिफिकेशन दाखवा"</string>
<string name="permdesc_postNotification" msgid="5974977162462877075">"ॲपला सूचना दाखवू देते"</string>
<string name="permlab_turnScreenOn" msgid="219344053664171492">"स्क्रीन सुरू करा"</string>
<string name="permdesc_turnScreenOn" msgid="4394606875897601559">"अॅपला स्क्रीन सुरू करण्याची परवानगी देते."</string>
@@ -1761,7 +1761,7 @@
<string name="owner_name" msgid="8713560351570795743">"मालक"</string>
<string name="guest_name" msgid="8502103277839834324">"अतिथी"</string>
<string name="error_message_title" msgid="4082495589294631966">"एरर"</string>
- <string name="error_message_change_not_allowed" msgid="843159705042381454">"या बदलास आपल्या प्रशासकाद्वारे अनुमती नाही"</string>
+ <string name="error_message_change_not_allowed" msgid="843159705042381454">"या बदलासाठी तुमच्या अॅडमिनची अनुमती नाही"</string>
<string name="app_not_found" msgid="3429506115332341800">"ही क्रिया हाताळण्यासाठी कोणताही ॲप्लिकेशन आढळला नाही"</string>
<string name="revoke" msgid="5526857743819590458">"मागे घ्या"</string>
<string name="mediasize_iso_a0" msgid="7039061159929977973">"ISO A0"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"चाचणी"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"सामुदायिक"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेअर करताना सुरक्षेसाठी अॅपमधील आशय लपवला आहे"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"उपग्रहाशी आपोआप कनेक्ट केलेले आहे"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"तुम्ही मोबाइल किंवा वाय-फाय नेटवर्कशिवाय मेसेज पाठवू आणि मिळवू शकता"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages उघडा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 4a4ea88..674eac3 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -706,10 +706,10 @@
<string name="face_acquired_too_far" msgid="2922278214231064859">"Dekatkan telefon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Tinggikan lagi telefon"</string>
<string name="face_acquired_too_low" msgid="4075391872960840081">"Rendahkan lagi telefon"</string>
- <string name="face_acquired_too_right" msgid="6245286514593540859">"Gerakkan telefon ke kiri anda"</string>
- <string name="face_acquired_too_left" msgid="9201762240918405486">"Gerakkan telefon ke kanan anda"</string>
+ <string name="face_acquired_too_right" msgid="6245286514593540859">"Gerakkan telefon ke kiri"</string>
+ <string name="face_acquired_too_left" msgid="9201762240918405486">"Gerakkan telefon ke kanan"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Sila lihat terus pada peranti anda."</string>
- <string name="face_acquired_not_detected" msgid="1057966913397548150">"Wajah tidak kelihatan. Pegang telefon pada paras mata."</string>
+ <string name="face_acquired_not_detected" msgid="1057966913397548150">"Wajah tidak kelihatan. Pegang telefon pada aras mata."</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Terlalu bnyk gerakan. Pegang telefon dgn stabil."</string>
<string name="face_acquired_recalibrate" msgid="8724013080976469746">"Sila daftarkan semula wajah anda."</string>
<string name="face_acquired_too_different" msgid="4505278456634706967">"Wajah tidak dikenali. Cuba lagi."</string>
@@ -844,7 +844,7 @@
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Memadamkan data tablet tanpa amaran dengan melakukan tetapan semula data kilang."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Padamkan data peranti Android TV anda tanpa amaran dengan melaksanakan tetapan semula data kilang."</string>
<string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Memadam data sistem maklumat hibur tanpa amaran dengan melakukan tetapan semula data kilang."</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Padamkan data telefon tanpa amaran dengan melakukan tetapan semula data kilang."</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Memadam data telefon tanpa amaran dengan melakukan tetapan semula data kilang."</string>
<string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Padam data profil"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Padam data pengguna"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Padam data pengguna ini pada tablet ini tanpa amaran."</string>
@@ -1030,7 +1030,7 @@
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"Anda telah mencuba untuk membuka kunci tablet secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Tablet kini akan ditetapkan semula ke tetapan lalai kilang."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"Anda telah cuba membuka peranti Android TV secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Peranti Android TV anda kini akan ditetapkan semula kepada tetapan lalai kilang."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Anda telah mencuba untuk membuka kunci telefon secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Telefon kini akan ditetapkan semula kepada tetapan lalai kilang."</string>
- <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Cuba lagi dalam <xliff:g id="NUMBER">%d</xliff:g> saat."</string>
+ <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Cuba lagi selepas <xliff:g id="NUMBER">%d</xliff:g> saat."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Lupa corak?"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Buka kunci akaun"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Terlalu banyak percubaan melukis corak"</string>
@@ -1517,7 +1517,7 @@
<string name="input_method_binding_label" msgid="1166731601721983656">"Kaedah input"</string>
<string name="sync_binding_label" msgid="469249309424662147">"Penyegerakan"</string>
<string name="accessibility_binding_label" msgid="1974602776545801715">"Kebolehaksesan"</string>
- <string name="wallpaper_binding_label" msgid="1197440498000786738">"Kertas dinding"</string>
+ <string name="wallpaper_binding_label" msgid="1197440498000786738">"Hiasan latar"</string>
<string name="chooser_wallpaper" msgid="3082405680079923708">"Tukar hiasan latar"</string>
<string name="notification_listener_binding_label" msgid="2702165274471499713">"Pendengar pemberitahuan"</string>
<string name="vr_listener_binding_label" msgid="8013112996671206429">"Pendengar VR"</string>
@@ -1726,7 +1726,7 @@
<string name="accessibility_service_warning_description" msgid="291674995220940133">"Kawalan penuh sesuai untuk apl yang membantu anda berkaitan dengan keperluan kebolehaksesan tetapi bukan untuk kebanyakan apl."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Melihat dan mengawal skrin"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Ciri ini boleh membaca semua kandungan pada skrin dan memaparkan kandungan di atas apl lain."</string>
- <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Lihat dan laksanakan tindakan"</string>
+ <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Melihat dan melaksanakan tindakan"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Ciri ini boleh menjejaki interaksi anda dengan apl atau penderia perkakasan dan berinteraksi dengan apl bagi pihak anda."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Benarkan"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Tolak"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Ujian"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Kandungan apl disembunyikan daripada perkongsian skrin untuk keselamatan"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Disambungkan secara automatik kepada satelit"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Anda boleh menghantar dan menerima mesej tanpa rangkaian mudah alih atau Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Buka Messages"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 8346134..030633b 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -995,7 +995,7 @@
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"မှန်ပါသည်"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ထပ် စမ်းပါ"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"ထပ် စမ်းပါ"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"ဝန်ဆောင်မှုနှင့် ဒေတာအားလုံးအတွက် လော့ခ်ဖွင့်ပါ"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"ဝန်ဆောင်မှုနှင့် ဒေတာအားလုံးသုံးရန် လော့ခ်ဖွင့်ပါ"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"မျက်မှာပြ လော့ခ်ဖွင့်ခြင်း ခွင့်ပြုသော အကြိမ်ရေထက် ကျော်လွန်သွားပါပြီ"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"ဆင်းမ်ကတ် မရှိပါ"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"တက်ဘလက်တွင် ဆင်းမ်ကတ်မရှိပါ။"</string>
@@ -1894,7 +1894,7 @@
<string name="clone_profile_label_badge" msgid="1871997694718793964">"<xliff:g id="LABEL">%1$s</xliff:g> ပုံတူပွား"</string>
<string name="private_profile_label_badge" msgid="1712086003787839183">"သီးသန့် <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"ပင်မဖြုတ်မီမှာ PIN ကို မေးကြည့်ရန်"</string>
- <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ပင်မဖြုတ်မီမှာ သော့ဖွင့် ရေးဆွဲမှုပုံစံကို မေးကြည့်ရန်"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ပင်မဖြုတ်မီ လော့ခ်ဖွင့်ပုံစံကို မေးရန်"</string>
<string name="lock_to_app_unlock_password" msgid="9126722403506560473">"ပင်မဖြုတ်မီမှာ စကားဝှက်ကို မေးကြည့်ရန်"</string>
<string name="package_installed_device_owner" msgid="7035926868974878525">"သင်၏ စီမံခန့်ခွဲသူက ထည့်သွင်းထားသည်"</string>
<string name="package_updated_device_owner" msgid="7560272363805506941">"သင်၏ စီမံခန့်ခွဲသူက အပ်ဒိတ်လုပ်ထားသည်"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"စမ်းသပ်မှု"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"အများသုံး"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"အက်ပ်အကြောင်းအရာသည် လုံခြုံရေးအတွက် မျက်နှာပြင် မျှဝေခြင်းမှ ဖျောက်ထားသည်"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"ဂြိုဟ်တုနှင့် အလိုအလျောက် ချိတ်ဆက်ထားသည်"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"မိုဘိုင်း (သို့) Wi-Fi ကွန်ရက်မရှိဘဲ မက်ဆေ့ဂျ်များကို ပို့နိုင်၊ လက်ခံနိုင်သည်"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ဖွင့်ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 252b413..b380199 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Felles"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av sikkerhetsgrunner er appinnholdet skjult for skjermdelingen"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automatisk tilkoblet satellitt"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Du kan sende og motta meldinger uten mobil- eller wifi-nettverk"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Åpne Meldinger"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 6035bb1..a486797 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -340,7 +340,7 @@
<string name="permgrouplab_sensors" msgid="9134046949784064495">"बडी सेन्सरहरू"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"तपाईंको महत्त्वपूर्ण संकेत बारे सेन्सर डेटा पहुँच गर्नुहोस्"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"सूचनाहरू"</string>
- <string name="permgroupdesc_notifications" msgid="4608679556801506580">"सूचनाहरू देखाइयोस्"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"सूचनाहरू देखाउनुहोस्"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"विन्डो सामग्रीको पुनःबहाली गर्नुहोस्।"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"तपाईँको अन्तरक्रिया भइरहेको विन्डोको सामग्रीको निरीक्षण गर्नुहोस्।"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"छोएर गरिने खोजलाई सुचारु गर्नुहोस्"</string>
@@ -362,7 +362,7 @@
<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="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>
@@ -395,7 +395,7 @@
<string name="permlab_receiveWapPush" msgid="4223747702856929056">"टेक्स्ट म्यासेजहरू (WAP) प्राप्त गर्नुहोस्"</string>
<string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP म्यासेजहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका म्यासेजहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string>
<string name="permlab_getTasks" msgid="7460048811831750262">"चलिरहेका एपहरू पुनःबहाली गर्नुहोस्"</string>
- <string name="permdesc_getTasks" msgid="7388138607018233726">"वर्तमानमा र भरखरै चलिरहेका कार्यहरू बारेको सूचना पुनःबहाली गर्न एपलाई अनुमित दिन्छ। यसले उपकरणमा प्रयोग भएका अनुप्रयोगहरूको बारेमा सूचना पत्ता लगाउन एपलाई अनुमति दिन सक्छ।"</string>
+ <string name="permdesc_getTasks" msgid="7388138607018233726">"वर्तमानमा र भरखरै चलिरहेका कार्यहरू बारेको सूचना पुनःबहाली गर्न एपलाई अनुमित दिन्छ। यसले उपकरणमा प्रयोग भएका एपहरूको बारेमा सूचना पत्ता लगाउन एपलाई अनुमति दिन सक्छ।"</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"प्रोफाइल र यन्त्र मालिकहरूको व्यवस्थापन गराउनुहोस्"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"एपहरूलाई प्रोफाइल र यन्त्र मालिकहरू सेट गर्ने अनुमति दिनुहोस्।"</string>
<string name="permlab_reorderTasks" msgid="7598562301992923804">"चलिरहेका एपहरूलाई पुनःक्रम गराउनुहोस्"</string>
@@ -403,7 +403,7 @@
<string name="permlab_enableCarMode" msgid="893019409519325311">"कार मोड सक्षम गर्नुहोस्"</string>
<string name="permdesc_enableCarMode" msgid="56419168820473508">"कार मोडलाई सक्षम पार्न एपलाई अनुमति दिन्छ।"</string>
<string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"एपहरू बन्द गर्नुहोस्"</string>
- <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"एपलाई अन्य अनुप्रयोगहरूको पृष्ठभूमि प्रक्रियाहरू बन्द गर्न अनुमति दिन्छ। यसले अन्य एपहरूलाई चल्नबाट रोक्न सक्दछ।"</string>
+ <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"एपलाई अन्य एपहरूको पृष्ठभूमि प्रक्रियाहरू बन्द गर्न अनुमति दिन्छ। यसले अन्य एपहरूलाई चल्नबाट रोक्न सक्दछ।"</string>
<string name="permlab_systemAlertWindow" msgid="5757218350944719065">"यो एप अन्य एपहरूमाथि देखा पर्न सक्छ"</string>
<string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"यो एप अन्य एपहरूमाथि वा स्क्रिनका अन्य भागहरूमा देखा पर्न सक्छ। यसले एपको सामान्य प्रयोगमा अवरोध पुर्याउन सक्छ र अन्य एपहरू देखा पर्ने तरिकालाई परिवर्तन गर्न सक्छ।"</string>
<string name="permlab_hideOverlayWindows" msgid="6382697828482271802">"एपका अन्य ओभरलेहरू लुकाउने अनुमति"</string>
@@ -452,7 +452,7 @@
<string name="permdesc_foregroundServiceSpecialUse" msgid="646713654541885919">"यसले एपलाई \"specialUse\" सँग सम्बन्धित फोरग्राउन्ड सेवाहरू प्रयोग गर्ने अनुमति दिन्छ"</string>
<string name="permlab_getPackageSize" msgid="375391550792886641">"एप भण्डारण ठाउँको मापन गर्नुहोस्"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"एपलाई यसको कोड, डेटा, र क्यास आकारहरू पुनःप्राप्त गर्न अनुमति दिन्छ।"</string>
- <string name="permlab_writeSettings" msgid="8057285063719277394">"प्रणाली सेटिङहरू परिमार्जन गर्नुहोस्"</string>
+ <string name="permlab_writeSettings" msgid="8057285063719277394">"सिस्टम सेटिङ परिमार्जन गर्नुहोस्"</string>
<string name="permdesc_writeSettings" msgid="8293047411196067188">"सिस्टमका सेटिङ डेटालाई परिवर्तन गर्नको लागि एपलाई अनुमति दिन्छ। खराब एपहरूले सायद तपाईँको प्रणालीको कन्फिगरेसनलाई क्षति पुर्याउन सक्छन्।"</string>
<string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"स्टार्टअपमा चलाउनुहोस्"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"आनुप्रयोगलाई प्रणाली बुट प्रक्रिया पूर्ण हुने बितिकै आफैलाई सुरु गर्ने अनुमति दिन्छ। यसले ट्याब्लेट सुरु गर्नमा ढिला गर्न सक्दछ र एपलाई समग्रमा ट्याब्लेट सधैँ चालु गरेर ढिला बनाउँदछ।"</string>
@@ -476,9 +476,9 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"आगमन तथा बहर्गमन डेटासहित तपाईँको ट्याब्लेटको कल लगको परिमार्जन गर्न एपलाई अनुमति दिन्छ। खराब एपहरूले यसलाई तपाईँको कल लग परिमार्जन गर्न वा मेटाउन प्रयोग गर्न सक्छन्।"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"एपलाई तपाईंको Android टिभी डिभाइसको आगमन र बहिर्गमन कलसम्बन्धी डेटासहित कल लग परिमार्जन गर्ने अनुमति दिन्छ। हानिकारक एपहरूले यसलाई तपाईंको कल लग मेटाउन वा परिमार्जन गर्न प्रयोग गर्न सक्छन्।"</string>
<string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"एपलाई तपाईंको फोनको आउने र बाहिर जाने कलहरूको बारेको डेटा सहित कल लग परिमार्जन गर्न अनुमति दिन्छ। खराब एपहरूले यसलाई तपाईंको कल लग मेटाउन वा परिमार्जन गर्न प्रयोग गर्न सक्दछ।"</string>
- <string name="permlab_bodySensors" msgid="662918578601619569">"प्रयोग गरिएका बेला हृदयको गति जस्ता बडी सेन्सरसम्बन्धी डेटा हेरियोस् र प्रयोग गरियोस्"</string>
+ <string name="permlab_bodySensors" msgid="662918578601619569">"प्रयोग गरिएका बेला हृदयको गति जस्ता बडी सेन्सरसम्बन्धी डेटा हेरियोस् र प्रयोग गर्नुहोस्"</string>
<string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"यसले यो एप प्रयोग गरिँदै गरेका बेला यसलाई हृदयको गति, शरीरको तापक्रम तथा रगतमा रहेको अक्सिजनको प्रतिशत जस्ता बडी सेन्सरसम्बन्धी डेटा हेर्ने तथा प्रयोग गर्ने अनुमति दिन्छ।"</string>
- <string name="permlab_bodySensors_background" msgid="4912560779957760446">"ब्याकग्राउन्डमा चलेका बेला हृदयको गति जस्ता बडी सेन्सरसम्बन्धी डेटा हेरियोस् र प्रयोग गरियोस्"</string>
+ <string name="permlab_bodySensors_background" msgid="4912560779957760446">"ब्याकग्राउन्डमा चलेका बेला हृदयको गति जस्ता बडी सेन्सरसम्बन्धी डेटा हेरियोस् र प्रयोग गर्नुहोस्"</string>
<string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"यसले यो एप ब्याकग्राउन्डमा चलेका बेला यसलाई हृदयको गति, शरीरको तापक्रम तथा रगतमा रहेको अक्सिजनको प्रतिशत जस्ता बडी सेन्सरसम्बन्धी डेटा हेर्ने तथा प्रयोग गर्ने अनुमति दिन्छ।"</string>
<string name="permlab_readCalendar" msgid="6408654259475396200">"पात्रोका कार्यक्रम र विवरणहरू पढ्ने"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"यस एपले तपाईंको ट्याब्लेटमा भण्डारण गरिएका पात्रो सम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।"</string>
@@ -615,7 +615,7 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"कुनै सम्बन्धित पासवर्ड सुरक्षा र किलकलाई असक्षम पार्न एपलाई अनुमति दिन्छ। उदाहरणको लागि, अन्तर्गमन फोन कल प्राप्त गर्दा फोनले किलकलाई असक्षम पार्छ, त्यसपछि कल सकिएको बेला किलक पुनःसक्षम पार्छ।"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"स्क्रिन लकको जटिलतासम्बन्धी जानकारी प्राप्त गर्ने अनुरोध गर्नुहोस्"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"यसले एपलाई स्क्रिन लकको जटिलताको स्तर (उच्च, मध्यम, न्यून वा कुनै पनि होइन) थाहा पाउने अनुमति दिन्छ जसले स्क्रिन लकको लम्बाइको सम्भावित दायरा र त्यसको प्रकारलाई जनाउँछ। यसै गरी, यो एपले प्रयोगकर्ताहरूलाई स्क्रिन लक अद्यावधिक गर्ने सुझाव पनि दिन सक्छ तर प्रयोगकर्ताहरू उक्त सुझावको बेवास्ता गरी बाहिर निस्कन सक्छन्। स्क्रिन लक सादा पाठको ढाँचामा भण्डारण नगरिने हुँदा यो एपलाई वास्तविक पासवर्ड थाहा नहुने कुराको हेक्का राख्नुहोस्।"</string>
- <string name="permlab_postNotification" msgid="4875401198597803658">"सूचनाहरू देखाइयोस्"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"सूचनाहरू देखाउनुहोस्"</string>
<string name="permdesc_postNotification" msgid="5974977162462877075">"यो एपलाई सूचना देखाउन दिनुहोस्"</string>
<string name="permlab_turnScreenOn" msgid="219344053664171492">"स्क्रिन अन गर्ने"</string>
<string name="permdesc_turnScreenOn" msgid="4394606875897601559">"यो एपलाई स्क्रिन अन गर्ने अनुमति दिन्छ।"</string>
@@ -782,35 +782,35 @@
<string name="permlab_control_incall_experience" msgid="6436863486094352987">"आउने-कल प्रयोगकर्ता अनुभव प्रदान गर्नुहोस्"</string>
<string name="permdesc_control_incall_experience" msgid="5896723643771737534">"एपलाई आउने-कल प्रयोगकर्ता अनुभव प्रदान गर्न अनुमति दिन्छ।"</string>
<string name="permlab_readNetworkUsageHistory" msgid="8470402862501573795">"नेटवर्क उपयोगको इतिहास पढ्नुहोस्"</string>
- <string name="permdesc_readNetworkUsageHistory" msgid="1112962304941637102">"निश्चित नेटवर्कहरू र अनुप्रयोगहरूको लागि ऐतिहासिक नेटवर्क उपयोग पढ्नको लागि एपलाई अनुमति दिन्छ।"</string>
+ <string name="permdesc_readNetworkUsageHistory" msgid="1112962304941637102">"निश्चित नेटवर्कहरू र एपहरूको लागि ऐतिहासिक नेटवर्क उपयोग पढ्नको लागि एपलाई अनुमति दिन्छ।"</string>
<string name="permlab_manageNetworkPolicy" msgid="6872549423152175378">"नेटवर्क नीति प्रबन्ध गर्नुहोस्"</string>
<string name="permdesc_manageNetworkPolicy" msgid="1865663268764673296">"नेटवर्क नीतिहरू व्यवस्थापन गर्न र एप-विशेष नियमहरू परिभाषित गर्न एपलाई अनुमति दिन्छ।"</string>
<string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"नेटवर्क उपयोग लेखालाई परिमार्जन गर्नुहोस्"</string>
- <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"एपलाई कसरी अनुप्रयोगहरूको विरूद्धमा कसरी नेटवर्क उपयोगी अकाउन्टेड छ भनेर परिमार्जन गर्न अनुमति दिन्छ। साधारण अनुप्रयोगहरूद्वारा प्रयोगको लागि होइन।"</string>
+ <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"एपलाई कसरी एपहरूको विरूद्धमा कसरी नेटवर्क उपयोगी अकाउन्टेड छ भनेर परिमार्जन गर्न अनुमति दिन्छ। साधारण अनुप्रयोगहरूद्वारा प्रयोगको लागि होइन।"</string>
<string name="permlab_accessNotifications" msgid="7130360248191984741">"सूचनाहरू पहुँच गर्नुहोस्"</string>
<string name="permdesc_accessNotifications" msgid="761730149268789668">"अन्य एपहरूबाट पोस्ट गरिएकासहित पुनःप्राप्त गर्न, परीक्षण गर्न र सूचनाहरू हटाउन एपहरूलाई अनुमति दिन्छ।"</string>
<string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"जानकारी श्रोता सेवामा बाँध्नुहोस्"</string>
<string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"होल्डरलाई सूचना श्रोता सेवाको शीर्ष-स्तरको इन्टरफेस बाँध्न अनुमति दिन्छ। सामान्य एपहरूलाई कहिले पनि आवश्यक नपर्न सक्दछ।"</string>
<string name="permlab_bindConditionProviderService" msgid="5245421224814878483">"सर्त प्रदायक सेवामा जोड्न"</string>
- <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"सर्त प्रदायक सेवाको माथिल्लो स्तरको इन्टरफेसमा जोड्न बाहकलाई अनुमति दिन्छ। साधारण अनुप्रयोगहरूको लागि कहिल्यै पनि आवश्यक पर्दैन।"</string>
+ <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"सर्त प्रदायक सेवाको माथिल्लो स्तरको इन्टरफेसमा जोड्न बाहकलाई अनुमति दिन्छ। साधारण एपहरूको लागि कहिल्यै पनि आवश्यक पर्दैन।"</string>
<string name="permlab_bindDreamService" msgid="4776175992848982706">"सपना सेवामा बाँध्नुहोस्"</string>
- <string name="permdesc_bindDreamService" msgid="9129615743300572973">"होल्डरलाई सपना सेवाको माथिल्लो स्तरको इन्टरफेसमा बाँध्न अनुमति दिन्छ। साधारण अनुप्रयोगहरूको लागि कहिल्यै पनि आवश्यक पर्दैन।"</string>
+ <string name="permdesc_bindDreamService" msgid="9129615743300572973">"होल्डरलाई सपना सेवाको माथिल्लो स्तरको इन्टरफेसमा बाँध्न अनुमति दिन्छ। साधारण एपहरूको लागि कहिल्यै पनि आवश्यक पर्दैन।"</string>
<string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"वाहक-प्रदान विन्यास एप सुरु गर्नुहोस्"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"प्रयोगकर्तालाई वाहक-प्रदान विन्यास एप सुरु गर्न अनुमति दिन्छ। साधारण एपहरूलाई कहिल्यै आवश्यक पर्ने छैन।"</string>
<string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"सञ्जाल अवस्थाका पर्यवेक्षणका लागि सुन्नुहोस्"</string>
<string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"सञ्जाल अवस्थाका पर्यवेक्षण सुन्नका लागि एपलाई अनुमति दिन्छ।सामान्य एपलाई चाँहिदै नचाँहिन सक्छ।"</string>
- <string name="permlab_setInputCalibration" msgid="932069700285223434">"इनपुट उपकरण क्यालिब्रेसन परिवर्तन गर्नुहोस्"</string>
- <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"एपलाई टच स्क्रीनको प्यारामिटरहरू क्यालिब्रेसन परिमार्जन गर्न अनुमति दिन्छ। साधारण अनुप्रयोगहरूको लागि कहिल्यै आवश्यक पर्दैन।"</string>
+ <string name="permlab_setInputCalibration" msgid="932069700285223434">"इनपुट डिभाइस क्यालिब्रेसन परिवर्तन गर्नुहोस्"</string>
+ <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"एपलाई टच स्क्रीनको प्यारामिटरहरू क्यालिब्रेसन परिमार्जन गर्न अनुमति दिन्छ। साधारण एपहरूको लागि कहिल्यै आवश्यक पर्दैन।"</string>
<string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"DRM प्रमाणपत्रको पहुँच"</string>
- <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"DRM प्रमाणपत्रहरू प्रावधान र प्रयोग गर्ने निवेदनको अनुमति दिन्छ। साधारण अनुप्रयोगहरूको लागि कहिल्यै पनि आवश्यक पर्दैन।"</string>
+ <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"DRM प्रमाणपत्रहरू प्रावधान र प्रयोग गर्ने निवेदनको अनुमति दिन्छ। साधारण एपहरूको लागि कहिल्यै पनि आवश्यक पर्दैन।"</string>
<string name="permlab_handoverStatus" msgid="7620438488137057281">"Android Beam स्थानान्तरण अवस्था प्राप्त गर्नुहोस्"</string>
<string name="permdesc_handoverStatus" msgid="3842269451732571070">"यस आवेदनले वर्तमान Android Beam स्थानान्तरण बारेमा जानकारी प्राप्त गर्न अनुमति दिन्छ"</string>
<string name="permlab_removeDrmCertificates" msgid="710576248717404416">"DRM सर्टिफिकेट हटाउनुहोस्"</string>
- <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"DRM प्रमाणपत्रहरू हटाउन एपलाई अनुमति दिन्छ। सामान्य अनुप्रयोगहरूको लागि कहिल्यै आवश्यकता पर्दैन।"</string>
+ <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"DRM प्रमाणपत्रहरू हटाउन एपलाई अनुमति दिन्छ। सामान्य एपहरूको लागि कहिल्यै आवश्यकता पर्दैन।"</string>
<string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"वाहक मेसेजिङ सेवामा आबद्ध हुनुहोस्"</string>
<string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"धारकलाई वाहक मेसेजिङ सेवाको उच्च-स्तरको इन्टरफेसमा आबद्ध हुन अनुमति दिनुहोस्। सामान्य एपहरूको लागि कहिल्यै आवश्यकता पर्दैन।"</string>
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"वाहक सेवाहरु बाँध्न"</string>
- <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"होल्डरलाई वाहक सेवाहरु बाँध्न अनुमति दिनुहोस्। सामान्य अनुप्रयोगहरूको लागि यो कहिल्यै आवश्यक पर्दैन।"</string>
+ <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"होल्डरलाई वाहक सेवाहरु बाँध्न अनुमति दिनुहोस्। सामान्य एपहरूको लागि यो कहिल्यै आवश्यक पर्दैन।"</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"बाधा नपुर्याउँनुहोस् पहुँच गर्नुहोस्"</string>
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"बाधा नपुर्याउँनुहोस् कन्फिगरेसन पढ्न र लेख्नको लागि एपलाई अनुमति दिनुहोस्।"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"हेर्ने अनुमतिको प्रयोग सुरु गर्नुहोस्"</string>
@@ -821,7 +821,7 @@
<string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"होल्डरलाई एपका सुविधासम्बन्धी जानकारी हेर्न दिन्छ।"</string>
<string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"नमुना लिने उच्च दरमा सेन्सरसम्बन्धी डेटा प्रयोग गर्ने"</string>
<string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"यो अनुमति दिइएमा एपले २०० हर्जभन्दा बढी दरमा सेन्सरसम्बन्धी डेटाको नमुना लिन सक्छ"</string>
- <string name="permlab_updatePackagesWithoutUserAction" msgid="3363272609642618551">"एप स्वतः अपडेट गरियोस्"</string>
+ <string name="permlab_updatePackagesWithoutUserAction" msgid="3363272609642618551">"एप स्वतः अपडेट गर्नुहोस्"</string>
<string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"तपाईंले यो अनुमति दिनुभयो भने होल्डरले पहिले नै इन्स्टल गरेको एप स्वतः अपडेट गर्न पाउँछ"</string>
<string name="permlab_writeVerificationStateE2eeContactKeys" msgid="3990742344778360457">"अन्य एपको स्वामित्वमा रहेका E2EE कन्ट्याक्ट कीहरूको प्रमाणीकरणको स्थिति अपडेट गर्ने"</string>
<string name="permdesc_writeVerificationStateE2eeContactKeys" msgid="8453156829747427041">"यसले एपलाई अन्य एपको स्वामित्वमा रहेका E2EE कन्ट्याक्ट कीहरूको प्रमाणीकरणको स्थिति अपडेट गर्न दिन्छ"</string>
@@ -830,11 +830,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"स्क्रिन अनलक गर्न गरिएको प्रयासको अनुगमन गर्ने"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप भएको संख्या निरीक्षण गर्नुहोस् र यदि निकै धेरै गलत पासवर्डहरू टाइप भएका छन भने ट्याब्लेट लक गर्नुहोस् वा ट्याब्लेटका सबै डेटा मेट्नुहोस्।"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android टिभी यन्त्र लक गर्नुहोस् वा डिभाइसमा भएको सम्पूर्ण डेटा मेटाउनुहोस्।"</string>
- <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"स्क्रिन अनलक गर्दा कति पटक गलत पासवर्ड टाइप गरिन्छ भन्ने कुरा निगरानी गरियोस् र अत्यन्तै धेरै पटक गलत पासवर्ड टाइप गरिएका खण्डमा यो इन्फोटेनमेन्ट प्रणाली लक गरियोस् वा यस इन्फोटेनमेन्ट प्रणालीका सबै डेटा मेटाइयोस्।"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"स्क्रिन अनलक गर्दा कति पटक गलत पासवर्ड टाइप गरिन्छ भन्ने कुरा निगरानी गर्नुहोस् र अत्यन्तै धेरै पटक गलत पासवर्ड टाइप गरिएका खण्डमा यो इन्फोटेनमेन्ट प्रणाली लक गर्नुहोस् वा यस इन्फोटेनमेन्ट प्रणालीका सबै डेटा मेटाउनुहोस्।"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"स्क्रिन अनलक गर्दा कति पटक गलत पासवर्ड टाइप भएको छ हेर्नुहोस् र निकै धेरै पटक गलत पासवर्ड टाइप भएको भने फोन लक गर्नुहोस् वा फोनका सबै डेटा मेट्नुहोस्।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप संख्या अनुगमन गर्नुहोस्, र यदि निकै धेरै गलत पासवर्डहरू टाइप गरिएमा ट्याब्लेट लक गर्नुहोस् वा प्रयोगकर्ताको डेटा मेटाउनुहोस्।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android टिभी यन्त्र लक गर्नुहोस् वा यो प्रयोगकर्ताको सम्पूर्ण डेटा मेटाउनुहोस्।"</string>
- <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"स्क्रिन अनलक गर्दा कति पटक गलत पासवर्ड टाइप गरिन्छ भन्ने कुरा निगरानी गरियोस् र अत्यन्तै धेरै पटक गलत पासवर्ड टाइप गरिएका खण्डमा यो इन्फोटेनमेन्ट प्रणाली लक गरियोस् वा यस प्रोफाइलका सबै डेटा मेटाइयोस्।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"स्क्रिन अनलक गर्दा कति पटक गलत पासवर्ड टाइप गरिन्छ भन्ने कुरा निगरानी गर्नुहोस् र अत्यन्तै धेरै पटक गलत पासवर्ड टाइप गरिएका खण्डमा यो इन्फोटेनमेन्ट प्रणाली लक गर्नुहोस् वा यस प्रोफाइलका सबै डेटा मेटाउनुहोस्।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप संख्या अनुगमन गर्नुहोस्, र यदि निकै धेरै गलत पासवर्डहरू टाइप गरिएमा फोन लक गर्नुहोस् वा प्रयोगकर्ताको डेटा मेटाउनुहोस्।"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"स्क्रिन लक परिवर्तन गर्ने"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"स्क्रिन लक परिवर्तन गर्नुहोस्।"</string>
@@ -843,13 +843,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"सबै डेटा मेट्ने"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"फ्याक्ट्रूी रिसेट गरेर चेतावनी नआउँदै ट्याबल्टको डेटा मेट्नुहोस्।"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"फ्याक्ट्री डेटा रिसेट गरेर चेतावनी नदिइकन आफ्नो Android टिभी डिभाइसको डेटा मेटाउनुहोस्।"</string>
- <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"यो इन्फोटेनमेन्ट प्रणालीको डेटा कुनै चेतावनीविनै फ्याक्ट्री डेटा रिसेट गरेर मेटाइयोस्।"</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"फ्याक्ट्रूी रिसेट गरेर चेतावनी नदिइकन फोनको डेटा मेट्न।"</string>
- <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"प्रोफाइल डेटा मेटाइयोस्"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"यो इन्फोटेनमेन्ट प्रणालीको डेटा कुनै चेतावनीविनै फ्याक्ट्री डेटा रिसेट गरेर मेटाउनुहोस्।"</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"फ्याक्ट्रूी रिसेट गरेर चेतावनी नदिइकन फोनको डेटा मेट्नुहोस्।"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"प्रोफाइल डेटा मेटाउनुहोस्"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"प्रयोगकर्ता डेटा मेट्नुहोस्"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"चेतावनी बिना यो ट्याब्लेटमा यस प्रयोगकर्ताको डेटा मेट्नुहोस्।"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"यो Android टिभी डिभाइसमा भएको यस प्रयोगकर्ताको डेटा चेतावनी नदिइकन मेटाउनुहोस्।"</string>
- <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"यो इन्फोटेनमेन्ट प्रणालीमा भएको यस प्रोफाइलको डेटा कुनै चेतावनीविनै मेटाइयोस्।"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"यो इन्फोटेनमेन्ट प्रणालीमा भएको यस प्रोफाइलको डेटा कुनै चेतावनीविनै मेटाउनुहोस्।"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"चेतावनी बिना यो फोनमा यस प्रयोगकर्ताको डेटा मेट्नुहोस्।"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"उपकरण विश्वव्यापी प्रोक्सी मिलाउनुहोस्"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"नीति सक्षम हुँदा प्रयोग गरिनको लागि यन्त्र ग्लोवल प्रोक्सी सेट गर्नुहोस्। केवल यन्त्र मालिकले ग्लोवल प्रोक्सी सेट गर्न सक्नुहुन्छ।"</string>
@@ -1405,7 +1405,7 @@
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"परीक्षण प्याकेज मोड सक्षम पारियो"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"परीक्षण प्याकेज मोड असक्षम पार्न फ्याक्ट्री रिसेट गर्नुहोस्।"</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"क्रमसम्बन्धी कन्सोल सक्षम पारियो"</string>
- <string name="console_running_notification_message" msgid="7892751888125174039">"कार्यसम्पादनमा प्रभाव परेको छ। यसलाई असक्षम पार्न बुटलोडरको जाँच गर्नुहोस्।"</string>
+ <string name="console_running_notification_message" msgid="7892751888125174039">"पर्फर्मेन्समा प्रभाव परेको छ। यसलाई असक्षम पार्न बुटलोडरको जाँच गर्नुहोस्।"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"परीक्षणका क्रममा रहेको MTE अन गरियो"</string>
<string name="mte_override_notification_message" msgid="2441170442725738942">"पर्फर्मेन्स र स्थिरता प्रभावित हुन सक्छ। अफ गर्न रिबुट गर्नुहोस्। तपाईंले arm64.memtag.bootctl प्रयोग गरी अन गर्नुभएको थियो भने अफ गर्नुअघि यसलाई परिवर्तन गरी \"कुनै पनि होइन\" बनाउनुहोस्।"</string>
<string name="usb_contaminant_detected_title" msgid="4359048603069159678">"USB पोर्टमा तरल पदार्थ वा धुलो भएको कुरा पत्ता लाग्यो"</string>
@@ -1419,14 +1419,14 @@
<string name="share_remote_bugreport_action" msgid="7630880678785123682">"सेयर गर्नुहोस्"</string>
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"अस्वीकार गर्नुहोस्"</string>
<string name="select_input_method" msgid="3971267998568587025">"निवेश विधि छान्नुहोस्"</string>
- <string name="show_ime" msgid="6406112007347443383">"फिजिकल किबोर्ड सक्रिय हुँदा यसलाई स्क्रिनमा राखियोस्"</string>
+ <string name="show_ime" msgid="6406112007347443383">"फिजिकल किबोर्ड सक्रिय हुँदा यसलाई स्क्रिनमा राख्नुहोस्"</string>
<string name="hardware" msgid="3611039921284836033">"अनस्क्रिन किबोर्ड प्रयोग गर्नुहोस्"</string>
<string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> कन्फिगर गर्नुहोस्"</string>
<string name="select_multiple_keyboards_layout_notification_title" msgid="6999491025126641938">"भौतिक किबोर्डहरू कन्फिगर गर्नुहोस्"</string>
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"भाषा र लेआउट चयन गर्न ट्याप गर्नुहोस्"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
- <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"अरू एपमाथि देखाइयोस्"</string>
+ <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"अरू एपमाथि देखाउनुहोस्"</string>
<string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> अन्य एपहरूमा देखिँदैछ"</string>
<string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> अन्य एपहरूमा देखिँदैछ"</string>
<string name="alert_windows_notification_message" msgid="6538171456970725333">"तपाईं <xliff:g id="NAME">%s</xliff:g> ले यो विशेषता प्रयोग नगरेको चाहनुहुन्न भने सेटिङहरू खोली यसलाई निष्क्रिय पार्न ट्याप गर्नुहोस्।"</string>
@@ -1718,11 +1718,11 @@
<string name="accessibility_shortcut_multiple_service_list" msgid="2128323171922023762">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"<xliff:g id="SERVICE">%1$s</xliff:g> प्रयोग गर्न सर्टकट अन गर्ने हो?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"केही सेकेन्डसम्म दुवै भोल्युम की थिचिराख्नुले <xliff:g id="SERVICE">%1$s</xliff:g> नामक पहुँचसम्बन्धी सुविधा सक्रिय गर्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nतपाईं सेटिङ > पहुँचमा गई यो सर्टकटमार्फत अर्को सुविधा खुल्ने बनाउन सक्नुहुन्छ।"</string>
- <string name="accessibility_shortcut_on" msgid="5463618449556111344">"सक्रिय गरियोस्"</string>
- <string name="accessibility_shortcut_off" msgid="3651336255403648739">"सक्रिय नगरियोस्"</string>
+ <string name="accessibility_shortcut_on" msgid="5463618449556111344">"सक्रिय गर्नुहोस्"</string>
+ <string name="accessibility_shortcut_off" msgid="3651336255403648739">"सक्रिय नगर्नुहोस्"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"सक्रिय"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"निष्क्रिय"</string>
- <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> लाई तपाईंको यन्त्र पूर्ण रूपमा नियन्त्रण गर्न दिने हो?"</string>
+ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> लाई तपाईंको डिभाइस पूर्ण रूपमा नियन्त्रण गर्न दिने हो?"</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"एक्सेसिबिलिटीसम्बन्धी आवश्यकतामा सहयोग गर्ने एपको पूर्ण नियन्त्रण गर्न दिनु उपयुक्त हुन्छ तर अधिकांश एपका हकमा यस्तो नियन्त्रण उपयुक्त हुँदैन।"</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"स्क्रिन हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"यसले स्क्रिनमा देखिने सबै सामग्री पढ्न सक्छ र अन्य एपहरूमा उक्त सामग्री देखाउन सक्छ।"</string>
@@ -1741,10 +1741,10 @@
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"सर्टकटलाई निष्क्रिय पार्नुहोस्"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"सर्टकट प्रयोग गर्नुहोस्"</string>
<string name="color_inversion_feature_name" msgid="2672824491933264951">"कलर इन्भर्सन"</string>
- <string name="color_correction_feature_name" msgid="7975133554160979214">"रङ सच्याउने सुविधा"</string>
+ <string name="color_correction_feature_name" msgid="7975133554160979214">"कलर करेक्सन"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"एक हाते मोड"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"अझै मधुरो"</string>
- <string name="hearing_aids_feature_name" msgid="1125892105105852542">"श्रवण यन्त्रहरू"</string>
+ <string name="hearing_aids_feature_name" msgid="1125892105105852542">"हियरिङ डिभाइसहरू"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन भयो।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अफ भयो।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"भोल्युम बटनहरू थिच्न छाड्नुहोस्। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन गर्न दुवै भोल्युम बटन फेरि ३ सेकेन्डसम्म थिचिराख्नुहोस्।"</string>
@@ -2167,7 +2167,7 @@
<string name="car_loading_profile" msgid="8219978381196748070">"लोड गर्दै"</string>
<string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # फाइल}other{{file_name} + # वटा फाइल}}"</string>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"कुनै पनि व्यक्तिसँग सेयर गर्ने सिफारिस गरिएको छैन"</string>
- <string name="chooser_all_apps_button_label" msgid="3230427756238666328">"अनुप्रयोगहरूको सूची"</string>
+ <string name="chooser_all_apps_button_label" msgid="3230427756238666328">"एपहरूको सूची"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"यो एपलाई रेकर्ड गर्ने अनुमति प्रदान गरिएको छैन तर यसले यो USB यन्त्रमार्फत अडियो क्याप्चर गर्न सक्छ।"</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"होम"</string>
<string name="accessibility_system_action_back_label" msgid="4205361367345537608">"पछाडि फर्कनुहोस्"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"परीक्षण"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"सामुदायिक"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रिन सेयर गर्दा सुरक्षाका लागि एपमा भएको सामग्री लुकाइएको छ"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"स्याटलाइटमा स्वतः कनेक्ट गरियो"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"तपाईं मोबाइल वा Wi-Fi नेटवर्कविनै म्यासेज पठाउन र प्राप्त गर्न सक्नुहुन्छ"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages खोल्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 5870ca3..d484405 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -725,7 +725,7 @@
<skip />
<string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Kan gezichtsmodel niet maken. Probeer het opnieuw."</string>
<string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Donkere bril waargenomen. Je gezicht moet helemaal zichtbaar zijn."</string>
- <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Gezichtsbedekking waargenomen. Je hele gezicht moet zichtbaar zijn."</string>
+ <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Gezichtsbedekking waargenomen. Je gezicht moet helemaal zichtbaar zijn."</string>
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Kan gezicht niet verifiëren. Hardware niet beschikbaar."</string>
@@ -830,12 +830,12 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pogingen voor schermontgrendeling bijhouden"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de tablet vergrendelen of alle gegevens op de tablet wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en het Android TV-apparaat vergrendelen of alle gegevens van het Android TV-apparaat wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
- <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Bijhouden hoe vaak onjuiste wachtwoorden worden getypt als het scherm wordt ontgrendeld, en het infotainmentsysteem vergrendelen of alle gegevens op het infotainmentsysteem wissen als te veel onjuiste wachtwoorden worden getypt."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld, en het infotainmentsysteem vergrendelen of alle gegevens op het infotainmentsysteem wissen bij te veel onjuiste wachtwoorden."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de telefoon vergrendelen of alle gegevens op de telefoon wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de tablet vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en het Android TV-apparaat vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
- <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Bijhouden hoe vaak onjuiste wachtwoorden worden getypt als het scherm wordt ontgrendeld, en het infotainmentsysteem vergrendelen of alle gegevens van dit profiel wissen als te veel onjuiste wachtwoorden worden getypt."</string>
- <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de telefoon vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd als het scherm wordt ontgrendeld, en het infotainmentsysteem vergrendelen of alle gegevens van dit profiel wissen bij te veel onjuiste wachtwoorden."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de telefoon vergrendelen of alle gegevens van deze gebruiker wissen bij te veel onjuiste wachtwoorden."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"De schermvergrendeling wijzigen"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Wijzig de schermvergrendeling."</string>
<string name="policylab_forceLock" msgid="7360335502968476434">"Het scherm vergrendelen"</string>
@@ -1894,7 +1894,7 @@
<string name="clone_profile_label_badge" msgid="1871997694718793964">"Kloon van <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="private_profile_label_badge" msgid="1712086003787839183">"Privé <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Vraag pin voor losmaken"</string>
- <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Vraag om ontgrendelingspatroon voor losmaken"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Ontgrendelingspatroon vragen om app los te maken"</string>
<string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Vraag wachtwoord voor losmaken"</string>
<string name="package_installed_device_owner" msgid="7035926868974878525">"Geïnstalleerd door je beheerder"</string>
<string name="package_updated_device_owner" msgid="7560272363805506941">"Geüpdatet door je beheerder"</string>
@@ -1902,7 +1902,7 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Met Batterijbesparing wordt het donkere thema aangezet en worden achtergrondactiviteit, bepaalde visuele effecten, bepaalde functies en sommige netwerkverbindingen beperkt of uitgezet."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"Met Batterijbesparing wordt het donkere thema aangezet en worden achtergrondactiviteit, bepaalde visuele effecten, bepaalde functies en sommige netwerkverbindingen beperkt of uitgezet."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"Databesparing beperkt het datagebruik door te voorkomen dat sommige apps gegevens sturen of ontvangen op de achtergrond. De apps die je open hebt, kunnen nog steeds data verbruiken, maar doen dit minder vaak. Afbeeldingen worden dan bijvoorbeeld niet getoond totdat je erop tikt."</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"Databesparing beperkt het datagebruik door te voorkomen dat sommige apps gegevens sturen of ontvangen op de achtergrond. De apps die je open hebt, kunnen nog steeds data verbruiken, maar doen dit minder vaak. Afbeeldingen worden bijvoorbeeld pas getoond als je erop tikt."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Databesparing aanzetten?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aanzetten"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Gedurende 1 minuut (tot {formattedTime})}other{Gedurende # minuten (tot {formattedTime})}}"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Gemeenschappelijk"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App-content verborgen voor scherm delen vanwege beveiligingsrisico\'s"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automatisch verbonden met satelliet"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Je kunt berichten sturen en krijgen zonder een mobiel of wifi-netwerk"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Berichten openen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 4174d04..df78cc9 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -831,7 +831,7 @@
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରିଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟାବଲେଟ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଟାବଲେଟ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ସ୍କ୍ରିନ୍ ଅନ୍ଲକ୍ କରିବା ସମୟରେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱାର୍ଡଗୁଡ଼ିକର ସଂଖ୍ୟାକୁ ନିରୀକ୍ଷଣ କରନ୍ତୁ ଏବଂ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଲକ୍ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱାର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଆପଣଙ୍କ Android TV ଡିଭାଇସ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
<string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ସ୍କ୍ରିନ ଅନଲକ କରିବା ସମୟରେ ଟାଇପ କରାଯାଇଥିବା ଭୁଲ ପାସୱାର୍ଡର ସଂଖ୍ୟାକୁ ମନିଟର କରନ୍ତୁ ଏବଂ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମକୁ ଲକ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକଗୁଡ଼ିଏ ଭୁଲ ପାସୱାର୍ଡ ଟାଇପ କରାଯାଇଥାଏ ତେବେ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମର ସମସ୍ତ ଡାଟା ଖାଲି କରନ୍ତୁ।"</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ଟାଇପ କରାଯାଇଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ। ସ୍କ୍ରିନ ଅନଲକ କରିବାବେଳେ ଏବଂ ଫୋନକୁ ଲକ କରିବା ସମୟରେ ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ କରାଯାଇଥାଏ, ତେବେ ଫୋନର ସମସ୍ତ ଡାଟା ଡିଲିଟ କରେ।"</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ଫୋନ ଅନଲକ କରିବା ବେଳେ ଟାଇପ କରାଯାଇଥିବା ଭୁଲ ପାସୱାର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରିବା ଏବଂ ଯଦି ଏକାଧିକ ଥର ଭୁଲ ପାସୱାର୍ଡ ଟାଇପ କରାଯାଇଥାଏ ତେବେ ଫୋନକୁ ଲକ କରିବା ବା ଫୋନର ସମସ୍ତ ଡାଟା ଇରେଜ କରିବା।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟାବଲେଟ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ସ୍କ୍ରିନ୍ ଅନ୍ଲକ୍ କରିବା ସମୟରେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱାର୍ଡଗୁଡ଼ିକର ସଂଖ୍ୟାକୁ ନିରୀକ୍ଷଣ କରନ୍ତୁ ଏବଂ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଲକ୍ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱାର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ସ୍କ୍ରିନ ଅନଲକ କରିବା ସମୟରେ ଟାଇପ କରାଯାଇଥିବା ଭୁଲ ପାସୱାର୍ଡର ସଂଖ୍ୟାକୁ ମନିଟର କରନ୍ତୁ ଏବଂ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମକୁ ଲକ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକଗୁଡ଼ିଏ ଭୁଲ ପାସୱାର୍ଡ ଟାଇପ କରାଯାଇଥାଏ ତେବେ ଏହି ପ୍ରୋଫାଇଲର ସମସ୍ତ ଡାଟା ଖାଲି କରନ୍ତୁ।"</string>
@@ -844,7 +844,7 @@
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ସେଟିଙ୍ଗ କରାଇ ଟାବ୍ଲେଟ୍ର ଡାଟା ଲିଭାଇଥାଏ।"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ଏକ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍ କରି ବିନା ଚେତାବନୀରେ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ର ଡାଟା ଲିଭାନ୍ତୁ।"</string>
<string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ଏକ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ କରି ବିନା ଚେତାବନୀରେ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମର ଡାଟା ଖାଲି କରନ୍ତୁ।"</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ କରି ଫୋନର ଡାଟା ଲିଭାଇଥାଏ।"</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ବିନା ଚେତାବନୀରେ ଫେକ୍ଟୋରୀ ଡାଟା ରିସେଟ କରି ଫୋନର ଡାଟା ଇରେଜ କରିବା।"</string>
<string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ପ୍ରୋଫାଇଲ ଡାଟା ଖାଲି କରନ୍ତୁ"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ୟୁଜର୍ ଡାଟା ଲିଭାନ୍ତୁ"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ବିନା ଚେତାବନୀରେ ଏହି ଟାବଲେଟରେ ଥିବା ଏହି ୟୁଜରଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
@@ -995,7 +995,7 @@
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ଠିକ୍!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"ସମସ୍ତ ସୁବିଧା ତଥା ଡାଟା ପାଇଁ ଅନଲକ୍ କରନ୍ତୁ"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"ସବୁ ଫିଚର ଓ ଡାଟା ପାଇଁ ଅନଲକ କରନ୍ତୁ"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"ସର୍ବାଧିକ ଫେସ୍ ଅନଲକ୍ ପ୍ରଚେଷ୍ଟା ଅତିକ୍ରମ କରିଛି"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"କୌଣସି SIM ନାହିଁ"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"ଟାବଲେଟରେ କୌଣସି SIM ନାହିଁ।"</string>
@@ -1754,7 +1754,7 @@
<string name="accessibility_button_instructional_text" msgid="8853928358872550500">"ଫିଚରଗୁଡ଼ିକ ମଧ୍ୟରେ ସ୍ୱିଚ୍ କରିବାକୁ ଆକ୍ସେସିବିଲିଟୀ ବଟନ୍ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ।"</string>
<string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"ଫିଚରଗୁଡ଼ିକ ମଧ୍ୟରେ ସ୍ୱିଚ୍ କରିବାକୁ ଦୁଇଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ୍ କରି ଧରି ରଖନ୍ତୁ।"</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"ଫିଚରଗୁଡ଼ିକ ମଧ୍ୟରେ ସ୍ୱିଚ୍ କରିବାକୁ, ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ୍ କରି ଧରି ରଖନ୍ତୁ।"</string>
- <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ମ୍ୟାଗ୍ନିଫିକେସନ୍"</string>
+ <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ମେଗ୍ନିଫିକେସନ"</string>
<string name="user_switched" msgid="7249833311585228097">"ବର୍ତ୍ତମାନର ୟୁଜର୍ ହେଉଛନ୍ତି <xliff:g id="NAME">%1$s</xliff:g>।"</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g>ରେ ସ୍ୱିଚ କରନ୍ତୁ…"</string>
<string name="user_logging_out_message" msgid="7216437629179710359">"<xliff:g id="NAME">%1$s</xliff:g>ଙ୍କୁ ଲଗଆଉଟ୍ କରାଯାଉଛି…"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"ଟେଷ୍ଟ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"କମ୍ୟୁନାଲ"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ସୁରକ୍ଷା ପାଇଁ ସ୍କ୍ରିନ ସେୟାରରୁ ଆପ ବିଷୟବସ୍ତୁକୁ ଲୁଚାଯାଇଛି"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"ସାଟେଲାଇଟ ସହ ସ୍ୱତଃ କନେକ୍ଟ ହୋଇଛି"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"ଏକ ମୋବାଇଲ କିମ୍ବା ୱାଇ-ଫାଇ ନେଟୱାର୍କ ବିନା ଆପଣ ମେସେଜ ପଠାଇପାରିବେ ଏବଂ ପାଇପାରିବେ"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ଖୋଲନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 3e7b27e..282694d 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -844,7 +844,7 @@
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ਇੱਕ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਟੈਬਲੈੱਟ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਤੁਹਾਡੇ Android TV ਡੀਵਾਈਸ ਦਾ ਡਾਟਾ ਮਿਟਾ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ।"</string>
<string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ਇੱਕ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਫ਼ੋਨ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰ ਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਫ਼ੋਨ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ਪ੍ਰੋਫਾਈਲ ਡਾਟਾ ਮਿਟਾਓ"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ਉਪਭੋਗਤਾ ਡਾਟਾ ਮਿਟਾਓ"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਇਸ ਟੈਬਲੈੱਟ ਤੇ ਮੌਜੂਦ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
@@ -1724,10 +1724,10 @@
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"ਬੰਦ"</string>
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"ਕੀ <xliff:g id="SERVICE">%1$s</xliff:g> ਨੂੰ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦਾ ਪੂਰਾ ਕੰਟਰੋਲ ਦੇਣਾ ਹੈ?"</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"ਪੂਰਾ ਕੰਟਰੋਲ ਉਨ੍ਹਾਂ ਐਪਾਂ ਲਈ ਢੁਕਵਾਂ ਹੈ ਜੋ ਪਹੁੰਚਯੋਗਤਾ ਸੰਬੰਧੀ ਲੋੜਾਂ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਕਰਦੀਆਂ ਹਨ, ਪਰ ਜ਼ਿਆਦਾਤਰ ਐਪਾਂ ਲਈ ਢੁਕਵਾਂ ਨਹੀਂ ਹੁੰਦਾ।"</string>
- <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"ਸਕ੍ਰੀਨ ਨੂੰ ਦੇਖੋ ਅਤੇ ਕੰਟਰੋਲ ਕਰੋ"</string>
+ <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"ਸਕ੍ਰੀਨ ਨੂੰ ਦੇਖਣਾ ਅਤੇ ਕੰਟਰੋਲ ਕਰਨਾ"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ਇਹ ਸਕ੍ਰੀਨ \'ਤੇ ਸਾਰੀ ਸਮੱਗਰੀ ਪੜ੍ਹ ਸਕਦੀ ਹੈ ਅਤੇ ਸਮੱਗਰੀ ਨੂੰ ਦੂਜੀਆਂ ਐਪਾਂ ਦੇ ਉੱਪਰ ਦਿਖਾ ਸਕਦੀ ਹੈ।"</string>
- <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"ਕਾਰਵਾਈਆਂ ਦੇਖੋ ਅਤੇ ਕਰੋ"</string>
- <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ਇਹ ਕਿਸੇ ਐਪ ਜਾਂ ਹਾਰਡਵੇਅਰ ਸੈਂਸਰ ਦੇ ਨਾਲ ਤੁਹਾਡੀਆਂ ਅੰਤਰਕਿਰਿਆਵਾਂ ਨੂੰ ਟਰੈਕ ਕਰ ਸਕਦੀ ਹੈ, ਅਤੇ ਤੁਹਾਡੀ ਤਰਫ਼ੋਂ ਐਪਾਂ ਦੇ ਨਾਲ ਅੰਤਰਕਿਰਿਆ ਕਰ ਸਕਦੀ ਹੈ।"</string>
+ <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"ਕਾਰਵਾਈਆਂ ਦੇਖਣਾ ਅਤੇ ਕਰਨਾ"</string>
+ <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ਇਹ ਕਿਸੇ ਐਪ ਜਾਂ ਹਾਰਡਵੇਅਰ ਸੈਂਸਰ ਦੇ ਨਾਲ ਤੁਹਾਡੀਆਂ ਅੰਤਰਕਿਰਿਆਵਾਂ ਨੂੰ ਟਰੈਕ ਕਰ ਸਕਦੀ ਹੈ ਅਤੇ ਤੁਹਾਡੀ ਤਰਫ਼ੋਂ ਐਪਾਂ ਦੇ ਨਾਲ ਅੰਤਰਕਿਰਿਆ ਕਰ ਸਕਦੀ ਹੈ।"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"ਕਰਨ ਦਿਓ"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"ਨਾ ਕਰਨ ਦਿਓ"</string>
<string name="accessibility_dialog_button_uninstall" msgid="2952465517671708108">"ਅਣਸਥਾਪਤ ਕਰੋ"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"ਜਾਂਚ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ਭਾਈਚਾਰਕ"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ਐਪ ਸਮੱਗਰੀ ਨੂੰ ਸੁਰੱਖਿਆ ਲਈ ਸਕ੍ਰੀਨ ਸਾਂਝਾਕਰਨ ਤੋਂ ਲੁਕਾਇਆ ਗਿਆ ਹੈ"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"ਸੈਟੇਲਾਈਟ ਨਾਲ ਸਵੈ-ਕਨੈਕਟ ਹੋਇਆ"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"ਤੁਸੀਂ ਮੋਬਾਈਲ ਜਾਂ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਤੋਂ ਬਿਨਾਂ ਸੁਨੇਹੇ ਭੇਜ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦੇ ਹੋ"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ਐਪ ਖੋਲ੍ਹੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ca126f7..f80e832 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -649,11 +649,11 @@
<string name="biometric_error_generic" msgid="6784371929985434439">"Podczas uwierzytelniania wystąpił błąd"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Używaj blokady ekranu"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Użyj blokady ekranu, aby kontynuować"</string>
- <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Mocno naciśnij czujnik"</string>
+ <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Mocno naciśnij czytnik"</string>
<string name="fingerprint_acquired_insufficient" msgid="2410176550915730974">"Nie rozpoznano odcisku palca. Spróbuj ponownie."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Wyczyść czytnik linii papilarnych i spróbuj ponownie"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Wyczyść czujnik i spróbuj ponownie"</string>
- <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Mocno naciśnij czujnik"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Mocno naciśnij czytnik"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Palec został obrócony zbyt wolno. Spróbuj ponownie."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Użyj odcisku innego palca"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Zbyt jasno"</string>
@@ -997,7 +997,7 @@
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Poprawnie!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Spróbuj ponownie."</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Spróbuj ponownie."</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"Odblokowanie wszystkich funkcji i danych"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"Odblokuj, by używać wszystkich funkcji i danych"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"Przekroczono maksymalną liczbę prób rozpoznania twarzy."</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"Brak karty SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"Brak karty SIM w tablecie."</string>
@@ -1763,7 +1763,7 @@
<string name="owner_name" msgid="8713560351570795743">"Właściciel"</string>
<string name="guest_name" msgid="8502103277839834324">"Gość"</string>
<string name="error_message_title" msgid="4082495589294631966">"Błąd"</string>
- <string name="error_message_change_not_allowed" msgid="843159705042381454">"Ta zmiana nie jest dozwolona przez administratora"</string>
+ <string name="error_message_change_not_allowed" msgid="843159705042381454">"Ta zmiana nie jest dozwolona przez administratora."</string>
<string name="app_not_found" msgid="3429506115332341800">"Nie znaleziono aplikacji do obsługi tej akcji"</string>
<string name="revoke" msgid="5526857743819590458">"Cofnij"</string>
<string name="mediasize_iso_a0" msgid="7039061159929977973">"ISO A0"</string>
@@ -1905,7 +1905,7 @@
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Oszczędzanie baterii uruchamia ciemny motyw oraz wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne, pewne funkcje oraz wybrane połączenia sieciowe."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"Oszczędzanie baterii uruchamia ciemny motyw oraz wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne, pewne funkcje oraz wybrane połączenia sieciowe."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Oszczędzanie danych uniemożliwia niektórym aplikacjom wysyłanie i odbieranie danych w tle, zmniejszając w ten sposób ich użycie. Aplikacja, z której w tej chwili korzystasz, może uzyskiwać dostęp do danych, ale rzadziej. Może to powodować, że obrazy będą się wyświetlać dopiero po kliknięciu."</string>
- <string name="data_saver_enable_title" msgid="7080620065745260137">"Włączyć Oszczędzanie danych?"</string>
+ <string name="data_saver_enable_title" msgid="7080620065745260137">"Włączyć oszczędzanie danych?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Włącz"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Przez 1 minutę (do {formattedTime})}few{Przez # minuty (do {formattedTime})}many{Przez # minut (do {formattedTime})}other{Przez # minuty (do {formattedTime})}}"</string>
<string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Przez 1 min (do {formattedTime})}few{Przez # min (do {formattedTime})}many{Przez # min (do {formattedTime})}other{Przez # min (do {formattedTime})}}"</string>
@@ -2396,8 +2396,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Testowy"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Wspólny"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ze względów bezpieczeństwa zawartość aplikacji jest niewidoczna podczas udostępniania ekranu"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automatycznie połączono z satelitą"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Możesz wymieniać wiadomości bez dostępu do sieci komórkowej lub Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otwórz Wiadomości"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index c53b1d5..3dcd619 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -726,7 +726,7 @@
<skip />
<string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Falha ao criar o modelo de rosto. Tente de novo."</string>
<string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Óculos escuros detectados. Seu rosto precisa estar completamente visível."</string>
- <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Máscara detectada. Seu rosto precisa estar visível."</string>
+ <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Tem algo cobrindo seu rosto. Ele precisa estar visível."</string>
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Impossível verificar rosto. Hardware indisponível."</string>
@@ -1371,8 +1371,8 @@
<string name="sim_added_message" msgid="6602906609509958680">"Reinicie o dispositivo para acessar a rede móvel."</string>
<string name="sim_restart_button" msgid="8481803851341190038">"Reiniciar"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Ativar serviço móvel"</string>
- <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Faça o download do app da operadora para ativar seu novo chip"</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Faça o download do app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar seu novo chip"</string>
+ <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Baixe o app da operadora para ativar seu novo chip"</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Baixe o app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar seu novo chip"</string>
<string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Fazer download do app"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"Novo chip inserido"</string>
<string name="carrier_app_notification_text" msgid="6567057546341958637">"Toque para configurar"</string>
@@ -1744,7 +1744,7 @@
<string name="color_inversion_feature_name" msgid="2672824491933264951">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="7975133554160979214">"Correção de cor"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo uma mão"</string>
- <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Escurecer a tela"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Escurecer ainda mais a tela"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"Aparelhos auditivos"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Público"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo do app oculto no compartilhamento de tela por motivos de segurança"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Conectado automaticamente ao satélite"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Você pode enviar e receber mensagens sem um dispositivo móvel ou uma rede Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir o app Mensagens"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 6b6f5c5..10e19cd 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -845,7 +845,7 @@
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Apagar os dados do tablet sem avisar através de uma reposição de dados de fábrica."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Apagar os dados do seu dispositivo Android TV sem avisar ao efetuar uma reposição de dados de fábrica."</string>
<string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Apague os dados do sistema de infoentretenimento sem aviso ao executar uma reposição de dados de fábrica."</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Apaga os dados do telemóvel sem avisar ao efetuar uma reposição de dados de fábrica."</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Apaga os dados do telemóvel sem avisar repondo os dados de fábrica."</string>
<string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Apague os dados do perfil"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Apagar os dados do utilizador"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Apagar os dados deste utilizador neste tablet sem aviso."</string>
@@ -1393,7 +1393,7 @@
<string name="usb_midi_notification_title" msgid="7404506788950595557">"O MIDI através de USB está ativado"</string>
<string name="usb_uvc_notification_title" msgid="2030032862673400008">"Dispositivo ligado como câmara Web"</string>
<string name="usb_accessory_notification_title" msgid="1385394660861956980">"Acessório USB ligado"</string>
- <string name="usb_notification_message" msgid="4715163067192110676">"Toque para obter mais opções."</string>
+ <string name="usb_notification_message" msgid="4715163067192110676">"Toque para ver mais opções."</string>
<string name="usb_power_notification_message" msgid="7284765627437897702">"A carregar o dispositivo ligado. Toque para obter mais opções."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Acessório de áudio analógico detetado"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"O dispositivo ligado não é compatível com este telemóvel. Toque para saber mais."</string>
@@ -1724,10 +1724,10 @@
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"ATIVADO"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"DESATIVADO"</string>
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"Permitir que o serviço <xliff:g id="SERVICE">%1$s</xliff:g> tenha controlo total sobre o seu dispositivo?"</string>
- <string name="accessibility_service_warning_description" msgid="291674995220940133">"O controlo total é adequado para aplicações que ajudam nas necessidades de acessibilidade, mas não para a maioria das apps."</string>
+ <string name="accessibility_service_warning_description" msgid="291674995220940133">"O controlo total é adequado para apps que ajudam nas necessidades de acessibilidade, mas não para a maioria das apps."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Ver e controlar o ecrã"</string>
- <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Pode ler todo o conteúdo do ecrã e sobrepor conteúdo a outras aplicações."</string>
- <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Veja e execute ações"</string>
+ <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Pode ler todo o conteúdo do ecrã e sobrepor conteúdo a outras apps."</string>
+ <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Ver e executar ações"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Pode monitorizar as suas interações com uma app ou um sensor de hardware e interagir com apps em seu nome."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permitir"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Recusar"</string>
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Comum"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo da app ocultado da partilha de ecrã por motivos de segurança"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Ligação de satélite estabelecida automaticamente"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Pode enviar e receber mensagens sem uma rede móvel ou Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abre a app Mensagens"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index c53b1d5..3dcd619 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -726,7 +726,7 @@
<skip />
<string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Falha ao criar o modelo de rosto. Tente de novo."</string>
<string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Óculos escuros detectados. Seu rosto precisa estar completamente visível."</string>
- <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Máscara detectada. Seu rosto precisa estar visível."</string>
+ <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Tem algo cobrindo seu rosto. Ele precisa estar visível."</string>
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Impossível verificar rosto. Hardware indisponível."</string>
@@ -1371,8 +1371,8 @@
<string name="sim_added_message" msgid="6602906609509958680">"Reinicie o dispositivo para acessar a rede móvel."</string>
<string name="sim_restart_button" msgid="8481803851341190038">"Reiniciar"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Ativar serviço móvel"</string>
- <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Faça o download do app da operadora para ativar seu novo chip"</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Faça o download do app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar seu novo chip"</string>
+ <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Baixe o app da operadora para ativar seu novo chip"</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Baixe o app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar seu novo chip"</string>
<string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Fazer download do app"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"Novo chip inserido"</string>
<string name="carrier_app_notification_text" msgid="6567057546341958637">"Toque para configurar"</string>
@@ -1744,7 +1744,7 @@
<string name="color_inversion_feature_name" msgid="2672824491933264951">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="7975133554160979214">"Correção de cor"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo uma mão"</string>
- <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Escurecer a tela"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Escurecer ainda mais a tela"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"Aparelhos auditivos"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Público"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo do app oculto no compartilhamento de tela por motivos de segurança"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Conectado automaticamente ao satélite"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Você pode enviar e receber mensagens sem um dispositivo móvel ou uma rede Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir o app Mensagens"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 5a449e3..e27603a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Comun"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conținutul aplicației este ascuns de permiterea accesului la ecran din motive de securitate"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"S-a conectat automat la satelit"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Poți să trimiți și să primești mesaje fără o rețea mobilă sau Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Deschide Mesaje"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 59982ef..a1da51e 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -653,7 +653,7 @@
<string name="fingerprint_acquired_insufficient" msgid="2410176550915730974">"Отпечаток пальца не распознан. Повторите попытку."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Очистите сканер отпечатков пальцев и повторите попытку."</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Очистите сканер и повторите попытку."</string>
- <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Плотно прижмите палец к сканеру."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Плотно прижмите палец к сканеру"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Вы перемещали палец слишком медленно. Повторите попытку."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Попробуйте сохранить отпечаток другого пальца."</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Слишком светло."</string>
@@ -833,7 +833,7 @@
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Отслеживает попытки ввода пароля при разблокировке экрана и блокирует планшетный ПК или удаляет с него все данные, если было сделано слишком много таких попыток."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Блокировать устройство Android TV или удалять с него все ваши данные при слишком большом количестве неудачных попыток ввести пароль для разблокировки экрана."</string>
<string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Блокировать информационно-развлекательную систему или удалять из нее все данные, если совершено слишком много неудачных попыток ввести пароль для разблокировки экрана."</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Отслеживает попытки ввода пароля при разблокировке экрана и блокирует телефон или удаляет с него все данные, если было сделано слишком много таких попыток."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Отслеживать попытки ввода пароля при разблокировке экрана и блокировать телефон или удалять с него все данные, если было сделано слишком много таких попыток."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Отслеживать неверно введенные пароли при разблокировке экрана и блокировать планшет или удалять с него все данные, если сделано слишком много неудачных попыток."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Блокировать устройство Android TV или удалять с него все данные этого пользователя при слишком большом количестве неудачных попыток ввести пароль для разблокировки экрана."</string>
<string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Блокировать информационно-развлекательную систему или удалять все данные профиля, если совершено слишком много неудачных попыток ввести пароль для разблокировки экрана."</string>
@@ -2396,8 +2396,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Тестовый"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Совместный"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Содержимое приложения исключено из демонстрации экрана в целях безопасности."</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Автоматически подключено к системам спутниковой связи"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Вы можете отправлять и получать сообщения без доступа к мобильной сети или Wi-Fi."</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Открыть Сообщения"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 04b2c52..5c9f4d9 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"පරීක්ෂණය"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"වාර්ගික"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ආරක්ෂාව සඳහා යෙදුම් අන්තර්ගතය තිරය බෙදා ගැනීමෙන් සඟවා ඇත"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"චන්ද්රිකාවට ස්වයංක්රීයව සම්බන්ධ වේ"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"ඔබට ජංගම හෝ Wi-Fi ජාලයක් නොමැතිව පණිවිඩ යැවීමට සහ ලැබීමට හැක"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages විවෘත කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 76cad30..4735866 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -727,7 +727,7 @@
<skip />
<string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Model tváre sa nedá vytvoriť. Skúste to znova."</string>
<string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Boli rozpoznané tmavé okuliare. Musí vám byť vidieť celú tvár."</string>
- <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Bolo rozpoznané rúško. Musí vám byť vidieť celú tvár."</string>
+ <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Máte čiastočne zakrytú tvár. Musí byť viditeľná celá."</string>
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Tvár sa nedá overiť. Hardvér nie je k dispozícii."</string>
@@ -833,7 +833,7 @@
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Sledovať počet nesprávnych hesiel zadaných pri odomykaní obrazovky a zamknúť tablet alebo vymazať všetky údaje tabletu v prípade príliš veľkého počtu neplatných pokusov o zadanie hesla."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Sledovanie počtu nesprávnych hesiel zadaných pri odomykaní obrazovky a v prípade, že ich je zadaných príliš mnoho, uzamknutie zariadenia Android TV alebo vymazanie všetkých jeho údajov."</string>
<string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Sledujte počet nesprávnych hesiel zadaných pri odomykaní obrazovky a uzamknite palubný systém alebo vymažte všetky údaje v palubnom systéme v prípade príliš veľkého počtu neplatných pokusov o zadanie hesla."</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Sledovať počet nesprávnych hesiel zadaných pri odomykaní obrazovky a zamknúť telefón alebo vymazať všetky údaje v telefóne v prípade príliš veľkého počtu neplatných pokusov o zadanie hesla."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Sleduje počet nesprávnych hesiel zadaných pri odomykaní obrazovky a uzamkne telefón alebo vymaže z telefónu všetky dáta, ak bolo zadaných príliš veľa nesprávnych hesiel."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Sledujte počet nesprávnych hesiel zadaných pri odomykaní obrazovky a v prípade, že ich je zadaných príliš mnoho, uzamknite tablet alebo vymažte všetky údaje tohto používateľa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Sledovanie počtu nesprávnych hesiel zadaných pri odomykaní obrazovky a ak je ich zadaných príliš mnoho, uzamknutie zariadenia Android TV alebo vymazanie všetkých údajov tohto používateľa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Sledujte počet nesprávnych hesiel zadaných pri odomykaní obrazovky a v prípade, že ich je zadaných príliš mnoho, uzamknite palubný systém alebo vymažte všetky údaje tohto profilu."</string>
@@ -846,7 +846,7 @@
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Bez predchádzajúceho upozornenia vymazať všetky dáta obnovením výrobných nastavení tabletu."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Vymazanie údajov v zariadení Android TV bez upozornenia obnovením výrobných nastavení."</string>
<string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Vymažte údaje palubného systému bez upozornenia obnovením výrobných nastavení."</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Bez predchádzajúceho upozornenia vymazať všetky dáta obnovením výrobných nastavení telefónu."</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Bez predchádzajúceho upozornenia vymaže všetky dáta obnovením výrobných nastavení telefónu."</string>
<string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Vymazanie údajov profilu"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Vymazať údaje používateľa"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Vymažte bez upozornenia údaje tohto používateľa na tomto tablete."</string>
@@ -2396,8 +2396,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Testovací"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Spoločný"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikácie bol na účely zabezpečenia skrytý v zdieľaní obrazovky"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automaticky pripojené k satelitu"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Správy môžete odosielať a prijímať bez mobilnej siete či siete Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvoriť Správy"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 5983929..9118c95 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -2396,8 +2396,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Preizkus"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Skupno"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Pri deljenju zaslona je vsebina aplikacije skrita zaradi varnosti"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Samodejno vzpostavljena povezava s satelitom"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Sporočila SMS lahko pošiljate in prejemate brez mobilnega omrežja ali omrežja Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Odpri Sporočila"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 5a02d56..6036a17 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"I përbashkët"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Përmbajtja e aplikacionit është fshehur nga ndarja e ekranit për arsye sigurie"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"U lidh automatikisht me satelitin"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Mund të dërgosh dhe të marrësh mesazhe pa një rrjet celular apo rrjet Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Hap \"Mesazhet\""</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 637371c..2020f91 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1223,7 +1223,7 @@
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"Измени"</string>
<string name="whichSendApplication" msgid="4143847974460792029">"Делите"</string>
<string name="whichSendApplicationNamed" msgid="4470386782693183461">"Делите помоћу апликације %1$s"</string>
- <string name="whichSendApplicationLabel" msgid="7467813004769188515">"Дели"</string>
+ <string name="whichSendApplicationLabel" msgid="7467813004769188515">"Дељење"</string>
<string name="whichSendToApplication" msgid="77101541959464018">"Пошаљите помоћу:"</string>
<string name="whichSendToApplicationNamed" msgid="3385686512014670003">"Пошаљите помоћу: %1$s"</string>
<string name="whichSendToApplicationLabel" msgid="3543240188816513303">"Пошаљи"</string>
@@ -2395,8 +2395,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Тест"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Заједничко"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Садржај апликације је скривен за дељење садржаја екрана због безбедности"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Аутоматски повезано са сателитом"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Можете да шаљете и примате поруке без мобилне или WiFi мреже"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Отвори Messages"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 2dde3a8..cd334a3 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -50,7 +50,7 @@
<item quantity="other">Du har <xliff:g id="NUMBER_1">%d</xliff:g> försök kvar innan SIM-kortet låses.</item>
<item quantity="one">Du har <xliff:g id="NUMBER_0">%d</xliff:g> försök kvar innan SIM-kortet låses.</item>
</plurals>
- <string name="imei" msgid="2157082351232630390">"IMEI-kod"</string>
+ <string name="imei" msgid="2157082351232630390">"IMEI"</string>
<string name="meid" msgid="3291227361605924674">"MEID"</string>
<string name="ClipMmi" msgid="4110549342447630629">"Nummerpresentatör för inkommande samtal"</string>
<string name="ClirMmi" msgid="6752346475055446417">"Dölj nummerpresentatör för utgående samtal"</string>
@@ -831,7 +831,7 @@
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås surfplattan eller ta bort alla data från surfplattan om för många felaktiga försök görs."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås Android TV-enheten eller rensa all data på Android TV-enheten om för många felaktiga lösenord har skrivits in."</string>
<string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås infotainmentsystemet eller rensa all data från infotainmentsystemet om för många felaktiga försök görs."</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Övervaka antalet felaktiga lösenord som angivits för skärmlåset och lås mobilen eller ta bort alla data från mobilen om för många felaktiga försök görs."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Övervaka antalet felaktiga lösenord som angivits för skärmlåset och lås telefonen eller ta bort alla data från telefonen om för många felaktiga försök görs."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås surfplattan eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås Android TV-enheten eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
<string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås infotainmentsystemet eller rensa all data från profilen om för många felaktiga lösenord har skrivits in."</string>
@@ -844,7 +844,7 @@
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Ta bort data från surfplattan utan förvarning genom att återställa standardinställningarna."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Radera data på Android TV-enheten utan förvarning genom att återställa standardinställningarna."</string>
<string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Rensa data från infotainmentsystemet utan förvarning genom att återställa standardinställningarna."</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ta bort data från mobilen utan förvarning genom att återställa standardinställningarna."</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ta bort data från telefonen utan förvarning genom att återställa standardinställningarna."</string>
<string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Rensa profildata"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Radera användaruppgifter"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Rensa användarens uppgifter på den här surfplattan utan förvarning."</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Allmän"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av säkerhetsskäl döljs appinnehållet vid skärmdelning"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Automatiskt ansluten till satellit"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Du kan skicka och ta emot meddelanden utan mobil- eller wifi-nätverk"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Öppna Messages"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 2936716..b16bbcd 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Jaribio"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Unaoshirikiwa"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Maudhui ya programu yamefichwa ili yasionekane kwenye skrini ya pamoja kwa sababu za kiusalama"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Imeunganishwa kiotomatiki na satelaiti"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Unaweza kutuma na kupokea ujumbe bila mtandao wa simu au Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Fungua Programu ya Messages"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 28ee91d..02e06fa 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -725,7 +725,7 @@
<skip />
<string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"முகத் தோற்றம் பதிவாகவில்லை. மீண்டும் முயலவும்."</string>
<string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"அடர் நிறக் கண்ணாடிகள் கண்டறியப்பட்டுள்ளது. உங்கள் முகத்தை முழுமையாகக் காட்டவும்."</string>
- <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"முகம் மறைக்கப்பட்டுள்ளது. உங்கள் முகத்தை முழுமையாகக் காட்டவும்."</string>
+ <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"முகத்தை மறைக்காமல் முழுமையாகக் காட்டவும்."</string>
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"முகத்தைச் சரிபார்க்க இயலவில்லை. வன்பொருள் இல்லை."</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"பரிசோதனை"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"பொது"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"பாதுகாப்பிற்காக, திரைப் பகிர்வில் இருந்து ஆப்ஸ் உள்ளடக்கம் மறைக்கப்பட்டுள்ளது"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"சாட்டிலைட்டுடன் தானாக இணைக்கப்பட்டது"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"மொபைல்/வைஃபை நெட்வொர்க் இல்லாமல் நீங்கள் மெசேஜ்களை அனுப்பலாம் பெறலாம்"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ஆப்ஸைத் திறக்கவும்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index c64c9b6d..ecd7e94 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -688,7 +688,7 @@
</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="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>
@@ -995,7 +995,7 @@
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"సరైనది!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"మళ్లీ ట్రై చేయండి"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"మళ్లీ ట్రై చేయండి"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"అన్ని లక్షణాలు మరియు డేటా కోసం అన్లాక్ చేయండి"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"అన్ని ఫీచర్ల కోసం, డేటా కోసం అన్లాక్ చేయండి"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"ఫేస్ అన్లాక్ ప్రయత్నాల గరిష్ఠ పరిమితిని మించిపోయారు"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"SIM లేదు"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"టాబ్లెట్లో SIM లేదు."</string>
@@ -1222,7 +1222,7 @@
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"ఎడిట్"</string>
<string name="whichSendApplication" msgid="4143847974460792029">"షేర్ చేయండి"</string>
<string name="whichSendApplicationNamed" msgid="4470386782693183461">"%1$sతో షేర్ చేయండి"</string>
- <string name="whichSendApplicationLabel" msgid="7467813004769188515">"షేర్ చేయి"</string>
+ <string name="whichSendApplicationLabel" msgid="7467813004769188515">"షేర్ చేయండి"</string>
<string name="whichSendToApplication" msgid="77101541959464018">"దీన్ని ఉపయోగించి పంపండి"</string>
<string name="whichSendToApplicationNamed" msgid="3385686512014670003">"%1$sని ఉపయోగించి పంపండి"</string>
<string name="whichSendToApplicationLabel" msgid="3543240188816513303">"పంపండి"</string>
@@ -1416,7 +1416,7 @@
<string name="share_remote_bugreport_notification_title" msgid="6708897723753334999">"బగ్ రిపోర్ట్ను షేర్ చేయాలా?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="3077385149217638550">"బగ్ రిపోర్ట్ను షేర్ చేస్తోంది..."</string>
<string name="share_remote_bugreport_notification_message_finished" msgid="7325635795739260135">"మీ అడ్మిన్ ఈ పరికరం సమస్యకు పరిష్కారాన్ని కనుగొనడంలో సహాయం కోసం బగ్ రిపోర్ట్ను రిక్వెస్ట్ చేశారు. యాప్లు మరియు డేటా షేర్ చేయబడవచ్చు."</string>
- <string name="share_remote_bugreport_action" msgid="7630880678785123682">"షేర్ చేయి"</string>
+ <string name="share_remote_bugreport_action" msgid="7630880678785123682">"షేర్ చేయండి"</string>
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"తిరస్కరిస్తున్నాను"</string>
<string name="select_input_method" msgid="3971267998568587025">"ఇన్పుట్ పద్ధతిని ఎంచుకోండి"</string>
<string name="show_ime" msgid="6406112007347443383">"దీన్ని భౌతిక కీబోర్డ్ యాక్టివ్గా ఉన్నప్పుడు స్క్రీన్పై ఉంచుతుంది"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"పరీక్ష"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"కమ్యూనల్"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"సెక్యూరిటీ కోసం స్క్రీన్ షేర్ నుండి యాప్ కంటెంట్ దాచబడింది"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"శాటిలైట్కు ఆటోమేటిక్గా కనెక్ట్ చేయబడింది"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"మీరు మొబైల్ లేదా Wi-Fi నెట్వర్క్ లేకుండా మెసేజ్లను పంపవచ్చు, స్వీకరించవచ్చు"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messagesను తెరవండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 7555f26..b1debfd 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"ทดสอบ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ส่วนกลาง"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ซ่อนเนื้อหาแอปจากการแชร์หน้าจอเพื่อความปลอดภัย"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"เชื่อมต่อกับดาวเทียมโดยอัตโนมัติ"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"คุณรับส่งข้อความผ่านดาวเทียมได้โดยไม่ต้องใช้เครือข่ายมือถือหรือ Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"เปิด Messages"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 112da5c..a6a6b50 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -709,7 +709,7 @@
<string name="face_acquired_too_right" msgid="6245286514593540859">"Iusog pakaliwa ang telepono mo"</string>
<string name="face_acquired_too_left" msgid="9201762240918405486">"Iusog pakanan ang telepono mo"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Tumingin nang mas direkta sa iyong device."</string>
- <string name="face_acquired_not_detected" msgid="1057966913397548150">"Hindi makita ang mukha mo. Hawakan ang telepono kapantay ng mata."</string>
+ <string name="face_acquired_not_detected" msgid="1057966913397548150">"Hindi makita ang mukha mo. Ipantay ang telepono sa mata."</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Masyadong magalaw. Hawakang mabuti ang telepono."</string>
<string name="face_acquired_recalibrate" msgid="8724013080976469746">"Paki-enroll muli ang iyong mukha."</string>
<string name="face_acquired_too_different" msgid="4505278456634706967">"Hindi nakilala ang mukha. Subukan ulit."</string>
@@ -1222,7 +1222,7 @@
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"I-edit"</string>
<string name="whichSendApplication" msgid="4143847974460792029">"Ibahagi"</string>
<string name="whichSendApplicationNamed" msgid="4470386782693183461">"Ibahagi gamit ang %1$s"</string>
- <string name="whichSendApplicationLabel" msgid="7467813004769188515">"Ibahagi"</string>
+ <string name="whichSendApplicationLabel" msgid="7467813004769188515">"I-share"</string>
<string name="whichSendToApplication" msgid="77101541959464018">"Ipadala gamit ang"</string>
<string name="whichSendToApplicationNamed" msgid="3385686512014670003">"Ipadala gamit ang %1$s"</string>
<string name="whichSendToApplicationLabel" msgid="3543240188816513303">"Ipadala"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nakatago ang content ng app mula sa pagbabahagi ng screen para sa seguridad"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Awtomatikong nakakonekta sa satellite"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Puwede kang magpadala at tumanggap ng mga mensahe nang walang mobile o Wi-Fi network"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Buksan ang Messages"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 2ca545b..7e34e4b 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1396,8 +1396,8 @@
<string name="usb_power_notification_message" msgid="7284765627437897702">"Bağlı cihaz şarj ediliyor. Diğer seçenekler için dokunun."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Analog ses aksesuarı algılandı"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Takılan cihaz bu telefonla uyumlu değil. Daha fazla bilgi edinmek için dokunun."</string>
- <string name="adb_active_notification_title" msgid="408390247354560331">"USB hata ayıklaması bağlandı"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"USB hata ayıklamayı kapatmak için dokunun"</string>
+ <string name="adb_active_notification_title" msgid="408390247354560331">"USB üzerinden hata ayıklama bağlandı"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"USB üzerinden hata ayıklamayı kapatmak için dokunun"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"USB hata ayıklamasını devre dışı bırakmak için seçin."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Kablosuz hata ayıklama bağlı"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Kablosuz hata ayıklamayı kapatmak için dokunun"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Paylaşılan"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Uygulama içerikleri, güvenlik nedeniyle ekran paylaşımında gizlendi"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Uyduya otomatik olarak bağlandı"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Mobil veya kablosuz ağa bağlı olmadan mesaj alıp gönderebilirsiniz"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Mesajlar\'ı aç"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index df2c715..34618cd 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1185,7 +1185,7 @@
<string name="deleteText" msgid="4200807474529938112">"Видалити"</string>
<string name="inputMethod" msgid="1784759500516314751">"Метод введення"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Дії з текстом"</string>
- <string name="error_handwriting_unsupported" msgid="7809438534946014050">"Рукописне введення не підтримується в цьому полі"</string>
+ <string name="error_handwriting_unsupported" msgid="7809438534946014050">"У цьому полі не підтримується рукописне введення"</string>
<string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Рукописне введення не підтримується в полях паролів"</string>
<string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string>
<string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Змінити метод введення"</string>
@@ -2396,8 +2396,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Тестовий профіль"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Спільний профіль"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"З міркувань безпеки вміст додатка приховано під час показу екрана"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Автоматично підключено до супутника"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Ви можете надсилати й отримувати повідомлення, не використовуючи Wi-Fi або мобільну мережу"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Відкрийте Повідомлення"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 29bd5be..caf0ca4 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"ٹیسٹ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"کمیونل"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"سیکیورٹی کے مد نظر ایپ کا مواد اسکرین کے اشتراک سے چھپا ہوا ہے"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"سٹلائٹ سے خودکار طور پر منسلک ہے"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"آپ موبائل یا Wi-Fi نیٹ ورک کے بغیر پیغامات بھیج اور موصول کر سکتے ہیں"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"پیغامات ایپ کو کھولیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index dddc3d6..e22c1dc 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Umumiy"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ekran namoyishida xavfsizlik maqsadida ilova kontenti berkitildi"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Sputnikka avtomatik ulandi"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Mobil yoki Wi-Fi tarmoqsiz xabarlarni yuborishingiz va qabul qilishingiz mumkin"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Xabarlar ilovasini ochish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 073a1a8..d6bd190 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -2178,9 +2178,9 @@
<string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"Khóa màn hình"</string>
<string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Chụp ảnh màn hình"</string>
<string name="accessibility_system_action_headset_hook_label" msgid="8524691721287425468">"Giá treo tai nghe"</string>
- <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"Phím tắt hỗ trợ tiếp cận trên màn hình"</string>
- <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Bộ chọn phím tắt hỗ trợ tiếp cận trên màn hình"</string>
- <string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Phím tắt hỗ trợ tiếp cận"</string>
+ <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"Lối tắt hỗ trợ tiếp cận trên màn hình"</string>
+ <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Bộ chọn lối tắt hỗ trợ tiếp cận trên màn hình"</string>
+ <string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Lối tắt hỗ trợ tiếp cận"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Đóng Ngăn thông báo"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Chuyển lên trên bằng bàn phím di chuyển"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Chuyển xuống dưới bằng bàn phím di chuyển"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Kiểm thử"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Dùng chung"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nội dung ứng dụng bị ẩn khỏi tính năng chia sẻ màn hình vì lý do bảo mật"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Đã tự động kết nối với vệ tinh"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Bạn có thể gửi và nhận tin nhắn mà không cần có mạng di động hoặc mạng Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Mở ứng dụng Tin nhắn"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ff2d4fc..9336c17 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -256,7 +256,7 @@
<string name="global_action_power_off" msgid="4404936470711393203">"关机"</string>
<string name="global_action_power_options" msgid="1185286119330160073">"电源"</string>
<string name="global_action_restart" msgid="4678451019561687074">"重启"</string>
- <string name="global_action_emergency" msgid="1387617624177105088">"紧急呼救"</string>
+ <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>
@@ -844,7 +844,7 @@
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"恢复出厂设置时,系统会在不发出警告的情况下清除平板电脑上的数据。"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"不事先发出警告就以恢复出厂设置的方式清空 Android TV 设备中的数据。"</string>
<string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"在不发出警告的情况下,通过恢复出厂设置来清除信息娱乐系统上的数据。"</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"恢复出厂设置时,系统会在不发出警告的情况下清除手机上的数据。"</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"恢复出厂设置,不发出警告就直接清除手机上的数据。"</string>
<string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"清除个人资料数据"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"清空用户数据"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"清空此用户在这台平板电脑上的数据,而不事先发出警告。"</string>
@@ -990,7 +990,7 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按 Menu 解锁或进行紧急呼救。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按 MENU 解锁。"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"绘制解锁图案"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"紧急呼救"</string>
+ <string name="lockscreen_emergency_call" msgid="7500692654885445299">"紧急呼叫"</string>
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通话"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正确!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"重试"</string>
@@ -1012,7 +1012,7 @@
<string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"停止"</string>
<string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"快退"</string>
<string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"快进"</string>
- <string name="emergency_calls_only" msgid="3057351206678279851">"只能拨打紧急呼救电话"</string>
+ <string name="emergency_calls_only" msgid="3057351206678279851">"只能拨打紧急呼叫电话"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"网络已锁定"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="2867953953604224166">"SIM 卡已用 PUK 码锁定。"</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"请参阅《用户指南》或与客服人员联系。"</string>
@@ -1894,7 +1894,7 @@
<string name="clone_profile_label_badge" msgid="1871997694718793964">"<xliff:g id="LABEL">%1$s</xliff:g>克隆"</string>
<string name="private_profile_label_badge" msgid="1712086003787839183">"私人“<xliff:g id="LABEL">%1$s</xliff:g>”"</string>
<string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"取消时要求输入PIN码"</string>
- <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"取消时要求绘制解锁图案"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"取消固定前要求绘制解锁图案"</string>
<string name="lock_to_app_unlock_password" msgid="9126722403506560473">"取消时要求输入密码"</string>
<string name="package_installed_device_owner" msgid="7035926868974878525">"已由您的管理员安装"</string>
<string name="package_updated_device_owner" msgid="7560272363805506941">"已由您的管理员更新"</string>
@@ -1977,7 +1977,7 @@
<string name="language_selection_title" msgid="52674936078683285">"添加语言"</string>
<string name="country_selection_title" msgid="5221495687299014379">"地区偏好设置"</string>
<string name="search_language_hint" msgid="7004225294308793583">"输入语言名称"</string>
- <string name="language_picker_section_suggested" msgid="6556199184638990447">"建议语言"</string>
+ <string name="language_picker_section_suggested" msgid="6556199184638990447">"建议的语言"</string>
<string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"推荐地区"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"建议的语言"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"建议的地区"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"测试"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"共用"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"为安全起见而在屏幕共享画面中处于隐藏状态的应用内容"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"自动连接到卫星"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"您无需使用移动网络或 WLAN 网络便能收发消息"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"打开“信息”应用"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 6f560bb..e92b7f0 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"測試"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"共用"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,應用程式內容已從分享螢幕畫面隱藏"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"已自動連線至衛星"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"你可在沒有流動/Wi-Fi 網絡的情況下收發訊息"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 8b2d534..124d17a 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1725,7 +1725,7 @@
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"要將裝置的完整控制權授予「<xliff:g id="SERVICE">%1$s</xliff:g>」嗎?"</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"如果你有無障礙服務需求,建議可將完整控制權授予具有相關功能的應用程式,但請勿將完整控制權授予大多數的應用程式。"</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"查看及控制螢幕"</string>
- <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"可讀取螢幕上的所有內容及在其他應用程式上顯示內容。"</string>
+ <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"可讀取螢幕上的所有內容,並在其他應用程式上顯示內容。"</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"查看及執行動作"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"可追蹤你與應用程式或硬體感應器的互動,並代表你與應用程式進行互動。"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"允許"</string>
@@ -1902,7 +1902,7 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"確定"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"省電模式會開啟深色主題,並限制或關閉背景活動、某些視覺效果、特定功能和部分網路連線。"</string>
<string name="battery_saver_description" msgid="8518809702138617167">"省電模式會開啟深色主題,並限制或關閉背景活動、某些視覺效果、特定功能和部分網路連線。"</string>
- <string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。你有一個使用中的應用程式仍可存取資料,但存取頻率可能會變低。舉例來說,圖片可能要等到你輕觸後才會顯示。"</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。目前使用中的單一應用程式仍可存取資料,但存取頻率可能會變低。舉例來說,圖片可能要等到你輕觸後才會顯示。"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟數據節省模式嗎?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{1 分鐘 (直到 {formattedTime})}other{# 分鐘 (直到 {formattedTime})}}"</string>
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"測試"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"通用"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,分享螢幕畫面未顯示應用程式內容"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"已自動連上衛星"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"你可以收發訊息,沒有行動/Wi-Fi 網路也無妨"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」應用程式"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 922172c..8ffb7a4 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -2394,8 +2394,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Hlola"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Okomphakathi"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for screen_not_shared_sensitive_content (7058419185079565001) -->
- <skip />
+ <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Okuqukethwe kwe-app kufihliwe kusuka ekwabelaneni kwesikrini ngokuvikelwa"</string>
<string name="satellite_notification_title" msgid="4026338973463121526">"Ixhumeke ngokuzenzakalelayo kusathelayithi"</string>
<string name="satellite_notification_summary" msgid="5207364139430767162">"Ungathumela futhi wamukele imilayezo ngaphandle kwenethiwekhi yeselula noma ye-Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Vula Imilayezo"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d4db244..a622d36 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4334,9 +4334,6 @@
-->
<bool name="config_wallpaperTopApp">false</bool>
- <!-- True if the device supports dVRR -->
- <bool name="config_supportsDvrr">false</bool>
-
<!-- True if the device supports at least one form of multi-window.
E.g. freeform, split-screen, picture-in-picture. -->
<bool name="config_supportsMultiWindow">true</bool>
@@ -7018,4 +7015,9 @@
<!-- Frame rate compatibility value for Wallpaper
FRAME_RATE_COMPATIBILITY_MIN (102) is used by default for lower power consumption -->
<integer name="config_wallpaperFrameRateCompatibility">102</integer>
+
+ <!-- Min time in milliseconds to complete an emergency gesture for it count.
+ If the gesture is completed faster than this, we assume it's not performed by human and the
+ event gets ignored. -->
+ <integer name="config_defaultMinEmergencyGestureTapDurationMillis">200</integer>
</resources>
diff --git a/core/res/res/values/config_battery_stats.xml b/core/res/res/values/config_battery_stats.xml
index e42962c..ae47899 100644
--- a/core/res/res/values/config_battery_stats.xml
+++ b/core/res/res/values/config_battery_stats.xml
@@ -32,6 +32,9 @@
devices-->
<integer name="config_defaultPowerStatsThrottlePeriodCpu">60000</integer>
+ <!-- Mobile Radio power stats collection throttle period in milliseconds. -->
+ <integer name="config_defaultPowerStatsThrottlePeriodMobileRadio">3600000</integer>
+
<!-- PowerStats aggregation period in milliseconds. This is the interval at which the power
stats aggregation procedure is performed and the results stored in PowerStatsStore. -->
<integer name="config_powerStatsAggregationPeriod">14400000</integer>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f3aa27f..461137c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1888,7 +1888,7 @@
<!-- Message shown when UDFPS fails to match -->
<string name="fingerprint_udfps_error_not_match">Fingerprint not recognized</string>
<!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
- <string name="fingerprint_dialog_use_fingerprint_instead">Can\u2019t recognize face. Use fingerprint instead.</string>
+ <string name="fingerprint_dialog_use_fingerprint_instead">Face not recognized. Use fingerprint instead.</string>
<!-- Accessibility message announced when a fingerprint has been authenticated [CHAR LIMIT=NONE] -->
<string name="fingerprint_authenticated">Fingerprint authenticated</string>
@@ -6457,4 +6457,16 @@
<string name="satellite_notification_how_it_works">How it works</string>
<!-- Initial/System provided label shown for an app which gets unarchived. [CHAR LIMIT=64]. -->
<string name="unarchival_session_app_label">Pending...</string>
+
+ <!-- Background user sound notification related messages -->
+ <!-- Notification title when sound comes from a call on background user [CHAR LIMIT=NOTIF_TITLE]-->
+ <string name="bg_user_sound_notification_title_call">Call for <xliff:g id="user_name" example="John Doe">%s</xliff:g></string>
+ <!-- Notification title when sound comes from an alarm or timer on background user [CHAR LIMIT=NOTIF_TITLE]-->
+ <string name="bg_user_sound_notification_title_alarm">Alarm for <xliff:g id="user_name" example="John Doe">%s</xliff:g></string>
+ <!-- Notification action button to prompt user switch to the background user [CHAR LIMIT=NONE] -->
+ <string name="bg_user_sound_notification_button_switch_user">Switch user</string>
+ <!-- Notification action button to mute the sound from the background user [CHAR LIMIT=NONE] -->
+ <string name="bg_user_sound_notification_button_mute">Mute</string>
+ <!-- Notification content action to mute the sound from the background user [CHAR LIMIT=NOTIF_BODY]-->
+ <string name="bg_user_sound_notification_message">Tap to mute sound</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f33e277..91d7ce0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -404,7 +404,6 @@
<java-symbol type="bool" name="config_supportAudioSourceUnprocessed" />
<java-symbol type="bool" name="config_freeformWindowManagement" />
<java-symbol type="bool" name="config_supportsBubble" />
- <java-symbol type="bool" name="config_supportsDvrr" />
<java-symbol type="bool" name="config_supportsMultiWindow" />
<java-symbol type="bool" name="config_supportsSplitScreenMultiWindow" />
<java-symbol type="bool" name="config_supportsMultiDisplay" />
@@ -5217,6 +5216,7 @@
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugHighBatteryLevel" />
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugAfterSignificantCharge" />
<java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodCpu" />
+ <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodMobileRadio" />
<java-symbol type="integer" name="config_powerStatsAggregationPeriod" />
<java-symbol type="integer" name="config_aggregatedPowerStatsSpanDuration" />
@@ -5380,6 +5380,13 @@
<java-symbol type="string" name="satellite_notification_how_it_works" />
<java-symbol type="drawable" name="ic_satellite_alt_24px" />
+ <!-- System notification for background user sound -->
+ <java-symbol type="string" name="bg_user_sound_notification_title_call" />
+ <java-symbol type="string" name="bg_user_sound_notification_title_alarm" />
+ <java-symbol type="string" name="bg_user_sound_notification_button_switch_user" />
+ <java-symbol type="string" name="bg_user_sound_notification_button_mute" />
+ <java-symbol type="string" name="bg_user_sound_notification_message" />
+
<!-- DisplayManager configs. -->
<java-symbol type="bool" name="config_evenDimmerEnabled" />
@@ -5401,4 +5408,6 @@
<!-- Frame rate compatibility value for Wallpaper -->
<java-symbol type="integer" name="config_wallpaperFrameRateCompatibility" />
+
+ <java-symbol type="integer" name="config_defaultMinEmergencyGestureTapDurationMillis" />
</resources>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index c8625b9..9bb72d9 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -28,7 +28,7 @@
standard SMS rate. The user is warned when the destination phone number matches the
"pattern" or "premium" regexes, and does not match the "free" or "standard" regexes. -->
- <!-- Harmonised European Short Codes are 6 digit numbers starting with 116 (free helplines).
+ <!-- Harmonised European Short Codes are 7 digit numbers starting with 116 (free helplines).
Premium patterns include short codes from: http://aonebill.com/coverage&tariffs
and http://mobilcent.com/info-worldwide.asp and extracted from:
http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
@@ -39,8 +39,8 @@
<!-- Albania: 5 digits, known short codes listed -->
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
- <!-- Argentina: 5 digits, known short codes listed -->
- <shortcode country="ar" pattern="\\d{5}" free="11711|28291|44077|78887" />
+ <!-- Argentina: 6 digits, known short codes listed -->
+ <shortcode country="ar" pattern="\\d{1,6}" free="11711|28291|44077|78887|191289|39010" />
<!-- Armenia: 3-5 digits, emergency numbers 10[123] -->
<shortcode country="am" pattern="\\d{3,5}" premium="11[2456]1|3024" free="10[123]|71522|71512|71502" />
@@ -67,7 +67,7 @@
<shortcode country="bh" pattern="\\d{1,5}" free="81181|85999" />
<!-- Brazil: 1-5 digits (standard system default, not country specific) -->
- <shortcode country="br" pattern="\\d{1,5}" free="6000[012]\\d|876|5500|9963|4141|8000" />
+ <shortcode country="br" pattern="\\d{1,5}" free="6000[012]\\d|876|5500|9963|4141|8000|2652" />
<!-- Belarus: 4 digits -->
<shortcode country="by" pattern="\\d{4}" premium="3336|4161|444[4689]|501[34]|7781" />
@@ -163,7 +163,7 @@
<shortcode country="in" pattern="\\d{1,5}" free="59336|53969" />
<!-- Indonesia: 1-5 digits (standard system default, not country specific) -->
- <shortcode country="id" pattern="\\d{1,5}" free="99477|6006|46645|363|93457" />
+ <shortcode country="id" pattern="\\d{1,5}" free="99477|6006|46645|363|93457|99265" />
<!-- Ireland: 5 digits, 5xxxx (50xxx=free, 5[12]xxx=standard), plus EU:
http://www.comreg.ie/_fileupload/publications/ComReg1117.pdf -->
@@ -172,6 +172,9 @@
<!-- Israel: 1-5 digits, known premium codes listed -->
<shortcode country="il" pattern="\\d{1,5}" premium="4422|4545" free="37477|6681" />
+ <!-- Iran: 4-6 digits, known premium codes listed -->
+ <shortcode country="ir" pattern="\\d{4,6}" free="700791|700792" />
+
<!-- Italy: 5 digits (premium=41xxx,42xxx), plus EU:
https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf -->
<shortcode country="it" pattern="\\d{5}" premium="44[0-4]\\d{2}|47[0-4]\\d{2}|48[0-4]\\d{2}|44[5-9]\\d{4}|47[5-9]\\d{4}|48[5-9]\\d{4}|455\\d{2}|499\\d{2}" free="116\\d{3}|4112503|40\\d{0,12}" standard="430\\d{2}|431\\d{2}|434\\d{4}|435\\d{4}|439\\d{7}" />
@@ -219,11 +222,11 @@
<!-- Mozambique: 1-5 digits (standard system default, not country specific) -->
<shortcode country="mz" pattern="\\d{1,5}" free="1714" />
- <!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
- <shortcode country="mx" pattern="\\d{4,6}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453|550346" />
+ <!-- Mexico: 4-7 digits (not confirmed), known premium codes listed -->
+ <shortcode country="mx" pattern="\\d{4,7}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453|550346|3030303" />
<!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
- <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668" />
+ <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668|66966" />
<!-- Namibia: 1-5 digits (standard system default, not country specific) -->
<shortcode country="na" pattern="\\d{1,5}" free="40005" />
@@ -255,6 +258,9 @@
<!-- Palestine: 5 digits, known premium codes listed -->
<shortcode country="ps" pattern="\\d{1,5}" free="37477|6681" />
+ <!-- Paraguay: 6 digits, known premium codes listed -->
+ <shortcode country="py" pattern="\\d{6}" free="191289" />
+
<!-- Poland: 4-5 digits (not confirmed), known premium codes listed, plus EU -->
<shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}|8012|80921" />
@@ -275,7 +281,7 @@
<shortcode country="ru" pattern="\\d{4}" premium="1(?:1[56]1|899)|2(?:09[57]|322|47[46]|880|990)|3[589]33|4161|44(?:4[3-9]|81)|77(?:33|81)|8424" free="6954|8501" standard="2037|2044"/>
<!-- Rwanda: 4 digits -->
- <shortcode country="rw" pattern="\\d{4}" free="5060" />
+ <shortcode country="rw" pattern="\\d{4}" free="5060|5061" />
<!-- Saudi Arabia -->
<shortcode country="sa" pattern="\\d{1,5}" free="8145" />
@@ -309,7 +315,10 @@
<shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
<!-- Tanzania: 1-5 digits (standard system default, not country specific) -->
- <shortcode country="tz" pattern="\\d{1,5}" free="15046|15234" />
+ <shortcode country="tz" pattern="\\d{1,5}" free="15046|15234|15324" />
+
+ <!-- Tunisia: 5 digits, known premium codes listed -->
+ <shortcode country="tn" pattern="\\d{5}" free="85799" />
<!-- Turkey -->
<shortcode country="tr" pattern="\\d{1,5}" free="7529|5528|6493|3193" />
@@ -324,8 +333,11 @@
visual voicemail code for T-Mobile: 122 -->
<shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245|611611|96831" />
- <!--Uruguay : 1-5 digits (standard system default, not country specific) -->
- <shortcode country="uy" pattern="\\d{1,5}" free="55002" />
+ <!--Uruguay : 1-6 digits (standard system default, not country specific) -->
+ <shortcode country="uy" pattern="\\d{1,6}" free="55002|191289" />
+
+ <!-- Venezuela: 1-6 digits (standard system default, not country specific) -->
+ <shortcode country="ve" pattern="\\d{1,6}" free="538352" />
<!-- Vietnam: 1-5 digits (standard system default, not country specific) -->
<shortcode country="vn" pattern="\\d{1,5}" free="5001|9055|8079" />
@@ -336,6 +348,9 @@
<!-- South Africa -->
<shortcode country="za" pattern="\\d{1,5}" free="44136|30791|36056|33009" />
+ <!-- Yemen -->
+ <shortcode country="ye" pattern="\\d{1,4}" free="5081" />
+
<!-- Zimbabwe -->
<shortcode country="zw" pattern="\\d{1,5}" free="33679" />
diff --git a/core/tests/FileSystemUtilsTest/Android.bp b/core/tests/FileSystemUtilsTest/Android.bp
new file mode 100644
index 0000000..53c22df
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/Android.bp
@@ -0,0 +1,78 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+ default_team: "trendy_team_android_kernel",
+}
+
+cc_library {
+ name: "libpunchtest",
+ stl: "none",
+ host_supported: true,
+ srcs: ["jni/android_test_jni_source.cpp"],
+ header_libs: ["jni_headers"],
+}
+
+android_test_helper_app {
+ name: "embedded_native_libs_test_app",
+ srcs: ["apk_embedded_native_libs/src/**/*.java"],
+ manifest: "apk_embedded_native_libs/embedded_native_libs_test_app.xml",
+ compile_multilib: "64",
+ jni_libs: [
+ "libpunchtest",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "platform-test-annotations",
+ ],
+ use_embedded_native_libs: true,
+}
+
+android_test_helper_app {
+ name: "extract_native_libs_test_app",
+ srcs: ["apk_extract_native_libs/src/**/*.java"],
+ manifest: "apk_extract_native_libs/extract_native_libs_test_app.xml",
+ compile_multilib: "64",
+ jni_libs: [
+ "libpunchtest",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "platform-test-annotations",
+ ],
+ use_embedded_native_libs: false,
+}
+
+java_test_host {
+ name: "FileSystemUtilsTests",
+ // Include all test java files
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "junit",
+ "platform-test-annotations",
+ "truth",
+ ],
+ libs: [
+ "tradefed",
+ "compatibility-host-util",
+ "compatibility-tradefed",
+ ],
+ data: [
+ ":embedded_native_libs_test_app",
+ ":extract_native_libs_test_app",
+ ],
+ test_suites: ["general-tests"],
+ test_config: "AndroidTest.xml",
+}
diff --git a/core/tests/FileSystemUtilsTest/AndroidManifest.xml b/core/tests/FileSystemUtilsTest/AndroidManifest.xml
new file mode 100644
index 0000000..acd5ef3
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:installLocation="internalOnly"
+ package="com.android.internal.content.fstests">
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.internal.content.fstests"
+ android:label="Frameworks FileSystemUtils Tests" />
+
+</manifest>
diff --git a/core/tests/FileSystemUtilsTest/AndroidTest.xml b/core/tests/FileSystemUtilsTest/AndroidTest.xml
new file mode 100644
index 0000000..27f49b2
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<configuration description="Runs FileSystemUtilsTest.">
+ <option name="test-suite-tag" value="apct"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="embedded_native_libs_test_app.apk" />
+ <option name="test-file-name" value="extract_native_libs_test_app.apk" />
+ </target_preparer>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="FileSystemUtilsTests.jar" />
+ </test>
+</configuration>
diff --git a/core/tests/FileSystemUtilsTest/TEST_MAPPING b/core/tests/FileSystemUtilsTest/TEST_MAPPING
new file mode 100644
index 0000000..89b3a7a
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "FileSystemUtilsTests"
+ }
+ ]
+}
diff --git a/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/embedded_native_libs_test_app.xml b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/embedded_native_libs_test_app.xml
new file mode 100644
index 0000000..868f7f3
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/embedded_native_libs_test_app.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.embedded">
+ <application android:extractNativeLibs="false">
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name=".MainActivity"
+ android:exported="true"
+ android:process=":NewProcess">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.embedded"/>
+</manifest>
\ No newline at end of file
diff --git a/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/MainActivity.java b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/MainActivity.java
new file mode 100644
index 0000000..efa2a39
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/MainActivity.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.embedded;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.VisibleForTesting;
+
+public class MainActivity extends Activity {
+
+ static {
+ System.loadLibrary("punchtest");
+ }
+
+ @VisibleForTesting
+ static final String INTENT_TYPE = "android.test.embedded.EMBEDDED_LIB_LOADED";
+
+ @VisibleForTesting
+ static final String KEY_OPERAND_1 = "OP1";
+
+ @VisibleForTesting
+ static final String KEY_OPERAND_2 = "OP2";
+
+ @VisibleForTesting
+ static final String KEY_RESULT = "RESULT";
+
+ @Override
+ public void onCreate(Bundle savedOnstanceState) {
+ super.onCreate(savedOnstanceState);
+
+ Intent received = getIntent();
+ int op1 = received.getIntExtra(KEY_OPERAND_1, -1);
+ int op2 = received.getIntExtra(KEY_OPERAND_2, -1);
+ int result = add(op1, op2);
+
+ // Send broadcast so that test can know app has launched and lib is loaded
+ // attach result which has been fetched from JNI lib
+ Intent intent = new Intent(INTENT_TYPE);
+ intent.putExtra(KEY_RESULT, result);
+ sendBroadcast(intent);
+ }
+
+ private native int add(int op1, int op2);
+}
diff --git a/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/PunchEmbeddedLibTest.java b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/PunchEmbeddedLibTest.java
new file mode 100644
index 0000000..d7d67b8
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/PunchEmbeddedLibTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.embedded;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class PunchEmbeddedLibTest {
+
+ @Test
+ public void testPunchedNativeLibs_embeddedLib() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ CountDownLatch receivedSignal = new CountDownLatch(1);
+
+ // Test app is expected to receive this and perform addition of operands using punched lib
+ int op1 = 48;
+ int op2 = 75;
+ IntentFilter intentFilter = new IntentFilter(MainActivity.INTENT_TYPE);
+ BroadcastReceiver broadcastReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ receivedSignal.countDown();
+ int result = intent.getIntExtra(MainActivity.KEY_RESULT, 1000);
+ Assert.assertEquals(result, op1 + op2);
+
+ }
+ };
+ context.registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED);
+
+ Intent launchIntent =
+ context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
+ launchIntent.putExtra(MainActivity.KEY_OPERAND_1, op1);
+ launchIntent.putExtra(MainActivity.KEY_OPERAND_2, op2);
+ context.startActivity(launchIntent);
+
+ Assert.assertTrue("Failed to launch app", receivedSignal.await(10, TimeUnit.SECONDS));
+ }
+}
diff --git a/core/tests/FileSystemUtilsTest/apk_extract_native_libs/extract_native_libs_test_app.xml b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/extract_native_libs_test_app.xml
new file mode 100644
index 0000000..6db96f7
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/extract_native_libs_test_app.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.extract">
+ <application android:extractNativeLibs="true">
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name=".MainActivity"
+ android:exported="true"
+ android:process=":NewProcess">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.extract"/>
+</manifest>
\ No newline at end of file
diff --git a/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/MainActivity.java b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/MainActivity.java
new file mode 100644
index 0000000..b1c157e
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/MainActivity.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.extract;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.VisibleForTesting;
+
+public class MainActivity extends Activity {
+
+ static {
+ System.loadLibrary("punchtest");
+ }
+
+ @VisibleForTesting
+ static final String INTENT_TYPE = "android.test.extract.EXTRACTED_LIB_LOADED";
+
+ @VisibleForTesting
+ static final String KEY_OPERAND_1 = "OP1";
+
+ @VisibleForTesting
+ static final String KEY_OPERAND_2 = "OP2";
+
+ @VisibleForTesting
+ static final String KEY_RESULT = "RESULT";
+
+ @Override
+ public void onCreate(Bundle savedOnstanceState) {
+ super.onCreate(savedOnstanceState);
+
+ Intent received = getIntent();
+ int op1 = received.getIntExtra(KEY_OPERAND_1, -1);
+ int op2 = received.getIntExtra(KEY_OPERAND_2, -1);
+ int result = subtract(op1, op2);
+
+ // Send broadcast so that test can know app has launched and lib is loaded
+ // attach result which has been fetched from JNI lib
+ Intent intent = new Intent(INTENT_TYPE);
+ intent.putExtra(KEY_RESULT, result);
+ sendBroadcast(intent);
+ }
+
+ private native int subtract(int op1, int op2);
+}
diff --git a/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/PunchExtractedLibTest.java b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/PunchExtractedLibTest.java
new file mode 100644
index 0000000..7cc1017
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/PunchExtractedLibTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.extract;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class PunchExtractedLibTest {
+
+ @Test
+ public void testPunchedNativeLibs_extractedLib() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ CountDownLatch receivedSignal = new CountDownLatch(1);
+
+ // Test app is expected to receive this and perform subtraction using extracted lib
+ int op1 = 100;
+ int op2 = 71;
+ IntentFilter intentFilter = new IntentFilter(MainActivity.INTENT_TYPE);
+ BroadcastReceiver broadcastReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ receivedSignal.countDown();
+ int result = intent.getIntExtra(MainActivity.KEY_RESULT, 1000);
+ Assert.assertEquals(result, op1 - op2);
+ }
+ };
+ context.registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED);
+
+ Intent launchIntent =
+ context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
+ launchIntent.putExtra(MainActivity.KEY_OPERAND_1, op1);
+ launchIntent.putExtra(MainActivity.KEY_OPERAND_2, op2);
+ context.startActivity(launchIntent);
+
+ Assert.assertTrue("Failed to launch app", receivedSignal.await(10, TimeUnit.SECONDS));
+ }
+}
diff --git a/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp b/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp
new file mode 100644
index 0000000..2a5ba81
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+
+// This will be called from embedded_native_libs_test_app
+extern "C" JNIEXPORT
+jint JNICALL Java_android_test_embedded_MainActivity_add(JNIEnv*, jclass, jint op1, jint op2) {
+ return op1 + op2;
+}
+
+// This will be called from extract_native_libs_test_app
+extern "C" JNIEXPORT
+jint JNICALL Java_android_test_extract_MainActivity_subtract(JNIEnv*, jclass, jint op1, jint op2) {
+ return op1 - op2;
+}
+
+// Initialize JNI
+jint JNI_OnLoad(JavaVM *jvm, void */* reserved */) {
+ JNIEnv *e;
+
+ // Check JNI version
+ if (jvm->GetEnv((void **) &e, JNI_VERSION_1_6)) {
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_6;
+}
diff --git a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
new file mode 100644
index 0000000..77802e5
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.content;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class FileSystemUtilsTest extends BaseHostJUnit4Test {
+
+ @Test
+ @AppModeFull
+ public void runPunchedApp_embeddedNativeLibs() throws DeviceNotAvailableException {
+ String appPackage = "android.test.embedded";
+ String testName = "PunchEmbeddedLibTest";
+ assertTrue(isPackageInstalled(appPackage));
+ runDeviceTests(appPackage, appPackage + "." + testName);
+ }
+
+ @Test
+ @AppModeFull
+ public void runPunchedApp_extractedNativeLibs() throws DeviceNotAvailableException {
+ String appPackage = "android.test.extract";
+ String testName = "PunchExtractedLibTest";
+ assertTrue(isPackageInstalled(appPackage));
+ runDeviceTests(appPackage, appPackage + "." + testName);
+ }
+}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 4808204..04e90ba 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -154,6 +154,12 @@
"android.test.runner",
"org.apache.http.legacy",
],
+ uses_libs: [
+ "android.test.runner",
+ ],
+ optional_uses_libs: [
+ "org.apache.http.legacy",
+ ],
sdk_version: "core_platform",
}
@@ -267,5 +273,10 @@
generate_get_transaction_name: true,
local_include_dirs: ["aidl"],
},
+ java_resources: [
+ "res/xml/power_profile_test.xml",
+ "res/xml/power_profile_test_cpu_legacy.xml",
+ "res/xml/power_profile_test_modem.xml",
+ ],
auto_gen_config: true,
}
diff --git a/core/tests/coretests/res/xml/power_profile_test.xml b/core/tests/coretests/res/xml/power_profile_test.xml
index 322ae05..7356c9e 100644
--- a/core/tests/coretests/res/xml/power_profile_test.xml
+++ b/core/tests/coretests/res/xml/power_profile_test.xml
@@ -98,4 +98,16 @@
<value>40</value>
<value>50</value>
</array>
-</device>
\ No newline at end of file
+
+ <!-- Idle current for bluetooth in mA.-->
+ <item name="bluetooth.controller.idle">0.02</item>
+
+ <!-- Rx current for bluetooth in mA.-->
+ <item name="bluetooth.controller.rx">3</item>
+
+ <!-- Tx current for bluetooth in mA-->
+ <item name="bluetooth.controller.tx">5</item>
+
+ <!-- Operating voltage for bluetooth in mV.-->
+ <item name="bluetooth.controller.voltage">3300</item>
+</device>
diff --git a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
index a5c8545..5765562 100644
--- a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
+++ b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
@@ -189,12 +189,16 @@
@Test
@EnableFlags(Flags.FLAG_MODES_API)
- public void builder_defaultTypeUnknown() {
+ public void builder_defaultsAreSensible() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("name",
Uri.parse("conditionId")).build();
assertThat(rule.getType()).isEqualTo(AutomaticZenRule.TYPE_UNKNOWN);
+ assertThat(rule.getInterruptionFilter()).isEqualTo(
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+ assertThat(rule.isEnabled()).isTrue();
}
+
@Test
@EnableFlags(Flags.FLAG_MODES_API)
public void validate_builderWithValidType_succeeds() throws Exception {
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
index 2ce7a7d..a0aff6e 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
@@ -16,7 +16,6 @@
package android.app.servertransaction;
-import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertEquals;
@@ -29,9 +28,6 @@
import android.app.Activity;
import android.app.ActivityThread;
import android.app.ClientTransactionHandler;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.RemoteException;
@@ -107,35 +103,6 @@
}
@Test
- public void testActivityConfigurationChangeItem_getContextToUpdate() {
- final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem
- .obtain(mActivityToken, mConfiguration, mActivityWindowInfo);
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(mActivity, context);
- }
-
- @Test
- public void testActivityRelaunchItem_getContextToUpdate() {
- final ActivityRelaunchItem item = ActivityRelaunchItem
- .obtain(mActivityToken, null /* pendingResults */, null /* pendingNewIntents */,
- 0 /* configChange */, mMergedConfiguration, false /* preserveWindow */,
- mActivityWindowInfo);
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(mActivity, context);
- }
-
- @Test
- public void testConfigurationChangeItem_getContextToUpdate() {
- final ConfigurationChangeItem item = ConfigurationChangeItem
- .obtain(mConfiguration, DEVICE_ID_DEFAULT);
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(ActivityThread.currentApplication(), context);
- }
-
- @Test
public void testDestroyActivityItem_preExecute() {
final DestroyActivityItem item = DestroyActivityItem
.obtain(mActivityToken, false /* finished */);
@@ -166,26 +133,6 @@
}
@Test
- public void testLaunchActivityItem_getContextToUpdate() {
- final LaunchActivityItem item = new TestUtils.LaunchActivityItemBuilder(
- mActivityToken, new Intent(), new ActivityInfo())
- .build();
-
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(ActivityThread.currentApplication(), context);
- }
-
- @Test
- public void testMoveToDisplayItem_getContextToUpdate() {
- final MoveToDisplayItem item = MoveToDisplayItem
- .obtain(mActivityToken, DEFAULT_DISPLAY, mConfiguration, mActivityWindowInfo);
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(mActivity, context);
- }
-
- @Test
public void testWindowContextInfoChangeItem_execute() {
final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
.obtain(mWindowClientToken, mConfiguration, DEFAULT_DISPLAY);
@@ -196,17 +143,6 @@
}
@Test
- public void testWindowContextInfoChangeItem_getContextToUpdate() {
- doReturn(mWindowContext).when(mHandler).getWindowContext(mWindowClientToken);
-
- final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
- .obtain(mWindowClientToken, mConfiguration, DEFAULT_DISPLAY);
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(mWindowContext, context);
- }
-
- @Test
public void testWindowContextWindowRemovalItem_execute() {
final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
mWindowClientToken);
@@ -220,7 +156,7 @@
final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
- true /* dragResizing */, mActivityToken, mActivityWindowInfo);
+ true /* dragResizing */, mActivityWindowInfo);
item.execute(mHandler, mPendingActions);
verify(mWindow).resized(mFrames,
@@ -228,16 +164,4 @@
true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
true /* dragResizing */, mActivityWindowInfo);
}
-
- @Test
- public void testWindowStateResizeItem_getContextToUpdate() {
- final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
- true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
- true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
- true /* dragResizing */, mActivityToken, mActivityWindowInfo);
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(ActivityThread.currentApplication(), context);
- }
-
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 77d31a5..f8c2d6a 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -23,11 +23,21 @@
import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
@@ -39,6 +49,7 @@
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.DisplayInfo;
import android.window.ActivityWindowInfo;
+import android.window.WindowTokenClient;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -49,6 +60,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -76,6 +88,13 @@
private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener;
@Mock
private IBinder mActivityToken;
+ @Mock
+ private Activity mActivity;
+ @Mock
+ private Resources mResources;
+
+ private Configuration mConfiguration;
+
private DisplayManagerGlobal mDisplayManager;
private Handler mHandler;
@@ -88,7 +107,12 @@
MockitoAnnotations.initMocks(this);
mDisplayManager = new DisplayManagerGlobal(mIDisplayManager);
mHandler = getInstrumentation().getContext().getMainThreadHandler();
- mController = ClientTransactionListenerController.createInstanceForTesting(mDisplayManager);
+ mController = spy(ClientTransactionListenerController
+ .createInstanceForTesting(mDisplayManager));
+
+ mConfiguration = new Configuration();
+ doReturn(mConfiguration).when(mResources).getConfiguration();
+ doReturn(mResources).when(mActivity).getResources();
}
@Test
@@ -107,6 +131,43 @@
}
@Test
+ public void testOnContextConfigurationChanged() {
+ doNothing().when(mController).onDisplayChanged(anyInt());
+ doReturn(123).when(mActivity).getDisplayId();
+
+ // Not trigger onDisplayChanged when there is no change.
+ mController.onContextConfigurationPreChanged(mActivity);
+ mController.onContextConfigurationPostChanged(mActivity);
+
+ verify(mController, never()).onDisplayChanged(anyInt());
+
+ mController.onContextConfigurationPreChanged(mActivity);
+ mConfiguration.windowConfiguration.setMaxBounds(new Rect(0, 0, 100, 200));
+ mController.onContextConfigurationPostChanged(mActivity);
+
+ verify(mController).onDisplayChanged(123);
+ }
+
+ @Test
+ public void testOnContextConfigurationChanged_duringClientTransaction() {
+ doNothing().when(mController).onDisplayChanged(anyInt());
+ doReturn(123).when(mActivity).getDisplayId();
+
+ // Not trigger onDisplayChanged until ClientTransaction finished execution.
+ mController.onClientTransactionStarted();
+
+ mController.onContextConfigurationPreChanged(mActivity);
+ mConfiguration.windowConfiguration.setMaxBounds(new Rect(0, 0, 100, 200));
+ mController.onContextConfigurationPostChanged(mActivity);
+
+ verify(mController, never()).onDisplayChanged(anyInt());
+
+ mController.onClientTransactionFinished();
+
+ verify(mController).onDisplayChanged(123);
+ }
+
+ @Test
public void testActivityWindowInfoChangedListener() {
mSetFlagsRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
@@ -125,4 +186,36 @@
verify(mActivityWindowInfoListener, never()).accept(any(), any());
}
+
+ @Test
+ public void testWindowTokenClient_onConfigurationChanged() {
+ doNothing().when(mController).onContextConfigurationPreChanged(any());
+ doNothing().when(mController).onContextConfigurationPostChanged(any());
+
+ final WindowTokenClient windowTokenClient = spy(new WindowTokenClient());
+ final Context context = mock(Context.class);
+ windowTokenClient.attachContext(context);
+
+ doReturn(mController).when(windowTokenClient).getClientTransactionListenerController();
+ doNothing().when(windowTokenClient).onConfigurationChangedInner(any(), any(), anyInt(),
+ anyBoolean());
+
+ // Not trigger when shouldReportConfigChange is false.
+ windowTokenClient.onConfigurationChanged(mConfiguration, 123 /* newDisplayId */,
+ false /* shouldReportConfigChange*/);
+
+ verify(mController, never()).onContextConfigurationPreChanged(any());
+ verify(mController, never()).onContextConfigurationPostChanged(any());
+
+ // Trigger in order when shouldReportConfigChange is true.
+ clearInvocations(windowTokenClient);
+ final InOrder inOrder = inOrder(mController, windowTokenClient);
+ windowTokenClient.onConfigurationChanged(mConfiguration, 123 /* newDisplayId */,
+ true /* shouldReportConfigChange*/);
+
+ inOrder.verify(mController).onContextConfigurationPreChanged(context);
+ inOrder.verify(windowTokenClient).onConfigurationChangedInner(context, mConfiguration,
+ 123 /* newDisplayId */, true /* shouldReportConfigChange*/);
+ inOrder.verify(mController).onContextConfigurationPostChanged(context);
+ }
}
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index 442394e3..0697c96 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -16,6 +16,8 @@
package android.os;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
@@ -24,6 +26,7 @@
import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.Presubmit;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
@@ -31,6 +34,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ParcelTest {
@@ -349,6 +354,50 @@
}
@Test
+ public void testClassCookies() {
+ Parcel p = Parcel.obtain();
+ assertThat(p.hasClassCookie(ParcelTest.class)).isFalse();
+
+ p.setClassCookie(ParcelTest.class, "string_cookie");
+ assertThat(p.hasClassCookie(ParcelTest.class)).isTrue();
+ assertThat(p.getClassCookie(ParcelTest.class)).isEqualTo("string_cookie");
+
+ p.removeClassCookie(ParcelTest.class, "string_cookie");
+ assertThat(p.hasClassCookie(ParcelTest.class)).isFalse();
+ assertThat(p.getClassCookie(ParcelTest.class)).isEqualTo(null);
+
+ p.setClassCookie(ParcelTest.class, "to_be_discarded_cookie");
+ p.recycle();
+ assertThat(p.getClassCookie(ParcelTest.class)).isNull();
+ }
+
+ @Test
+ public void testClassCookies_removeUnexpected() {
+ Parcel p = Parcel.obtain();
+
+ assertLogsWtf(() -> p.removeClassCookie(ParcelTest.class, "not_present"));
+
+ p.setClassCookie(ParcelTest.class, "value");
+
+ assertLogsWtf(() -> p.removeClassCookie(ParcelTest.class, "different"));
+ assertThat(p.getClassCookie(ParcelTest.class)).isNull(); // still removed
+
+ p.recycle();
+ }
+
+ private static void assertLogsWtf(Runnable test) {
+ ArrayList<Log.TerribleFailure> wtfs = new ArrayList<>();
+ Log.TerribleFailureHandler oldHandler = Log.setWtfHandler(
+ (tag, what, system) -> wtfs.add(what));
+ try {
+ test.run();
+ } finally {
+ Log.setWtfHandler(oldHandler);
+ }
+ assertThat(wtfs).hasSize(1);
+ }
+
+ @Test
@IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testHasBinders_AfterWritingBinderToParcel() {
Binder binder = new Binder();
@@ -360,7 +409,6 @@
assertTrue(pA.hasBinders());
}
-
@Test
@IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testHasBindersInRange_AfterWritingBinderToParcel() {
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 97f894f..4fb85c1f 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -25,9 +25,7 @@
import static android.view.InsetsSource.ID_IME;
import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED;
import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY;
-import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
import static android.view.WindowInsets.Type.all;
-import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsets.Type.defaultVisible;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
@@ -49,7 +47,6 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -805,48 +802,6 @@
}
@Test
- public void testCaptionInsetsStateAssemble() {
- if (CAPTION_ON_SHELL) {
- // For this case, the test is covered by WindowContainerInsetsSourceProviderTest, This
- // test can be removed after the caption is moved to shell completely.
- return;
- }
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- final Rect frame = new Rect(0, 0, 100, 300);
- final int captionBarHeight = 100;
- final InsetsState state = mController.getState();
- mController.onFrameChanged(frame);
- mController.setCaptionInsetsHeight(captionBarHeight);
- // The caption bar insets height should be the same as the caption bar height.
- assertEquals(captionBarHeight, state.calculateInsets(frame, captionBar(), false).top);
- // Test update to remove the caption bar
- mController.setCaptionInsetsHeight(0);
- // The caption bar source should not be there at all, because we don't add empty
- // caption to the state from the server.
- for (int i = state.sourceSize() - 1; i >= 0; i--) {
- assertNotEquals(captionBar(), state.sourceAt(i).getType());
- }
- });
- }
-
- @Test
- public void testNotifyCaptionInsetsOnlyChange() {
- if (CAPTION_ON_SHELL) {
- // For this case, the test is covered by WindowContainerInsetsSourceProviderTest, This
- // test can be removed after the caption is moved to shell completely.
- return;
- }
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- reset(mTestHost);
- mController.setCaptionInsetsHeight(100);
- verify(mTestHost).notifyInsetsChanged();
- reset(mTestHost);
- mController.setCaptionInsetsHeight(0);
- verify(mTestHost).notifyInsetsChanged();
- });
- }
-
- @Test
public void testRequestedState() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.hide(statusBars() | navigationBars());
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index dcfbf64..0bf9a4c 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -19,7 +19,6 @@
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
-import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY;
import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
@@ -27,18 +26,23 @@
import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly;
import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly;
import static android.view.flags.Flags.toolkitFrameRateSmallUsesPercentReadOnly;
+import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.annotation.NonNull;
import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
import android.os.SystemClock;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.DisplayMetrics;
+import android.widget.FrameLayout;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
@@ -69,6 +73,8 @@
private Activity mActivity;
private View mMovingView;
private ViewRootImpl mViewRoot;
+ private CountDownLatch mAfterDrawLatch;
+ private Throwable mAfterDrawThrowable;
@Before
public void setUp() throws Throwable {
@@ -82,23 +88,34 @@
parent = parent.getParent();
}
mViewRoot = (ViewRootImpl) parent;
+ mAfterDrawThrowable = null;
}
- @UiThreadTest
@Test
@RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
- public void frameRateChangesWhenContentMoves() {
- mMovingView.offsetLeftAndRight(100);
- float frameRate = mViewRoot.getPreferredFrameRate();
- assertTrue(frameRate > 0);
+ public void frameRateChangesWhenContentMoves() throws Throwable {
+ waitForFrameRateCategoryToSettle();
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.offsetLeftAndRight(100);
+ runAfterDraw(() -> {
+ if (toolkitFrameRateVelocityMappingReadOnly()) {
+ float frameRate = mViewRoot.getLastPreferredFrameRate();
+ assertTrue(frameRate > 0);
+ } else {
+ assertEquals(FRAME_RATE_CATEGORY_HIGH,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ }
+ });
+ });
+ waitForAfterDraw();
}
@UiThreadTest
@Test
@RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
public void firstFrameNoMovement() {
- assertEquals(0f, mViewRoot.getPreferredFrameRate(), 0f);
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate(), 0f);
}
@Test
@@ -141,9 +158,34 @@
mActivityRule.runOnUiThread(() -> {
mMovingView.setFrameContentVelocity(1f);
mMovingView.invalidate();
- assertEquals(60f, mViewRoot.getPreferredFrameRate(), 0f);
- assertEquals(FRAME_RATE_COMPATIBILITY_GTE, mViewRoot.getFrameRateCompatibility());
+ runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f));
});
+ waitForAfterDraw();
+ }
+
+ @Test
+ @RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API,
+ FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
+ public void velocityWithChildMovement() throws Throwable {
+ FrameLayout frameLayout = new FrameLayout(mActivity);
+ mActivityRule.runOnUiThread(() -> {
+ ViewGroup.LayoutParams fullSize = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ mActivity.setContentView(frameLayout, fullSize);
+ if (mMovingView.getParent() instanceof ViewGroup) {
+ ((ViewGroup) mMovingView.getParent()).removeView(mMovingView);
+ }
+ frameLayout.addView(mMovingView, fullSize);
+ });
+ waitForFrameRateCategoryToSettle();
+ mActivityRule.runOnUiThread(() -> {
+ frameLayout.setFrameContentVelocity(1f);
+ mMovingView.offsetTopAndBottom(100);
+ runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f));
+ });
+ waitForAfterDraw();
}
@Test
@@ -161,23 +203,11 @@
mActivityRule.runOnUiThread(() -> {
mMovingView.setFrameContentVelocity(1_000_000_000f);
mMovingView.invalidate();
- assertEquals(140f, mViewRoot.getPreferredFrameRate(), 0f);
- assertEquals(FRAME_RATE_COMPATIBILITY_GTE, mViewRoot.getFrameRateCompatibility());
- });
- }
-
- private void waitForFrameRateCategoryToSettle() throws Throwable {
- for (int i = 0; i < 5; i++) {
- final CountDownLatch drawLatch = new CountDownLatch(1);
-
- // Now that it is small, any invalidation should have a normal category
- mActivityRule.runOnUiThread(() -> {
- mMovingView.invalidate();
- mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch::countDown);
+ runAfterDraw(() -> {
+ assertEquals(140f, mViewRoot.getLastPreferredFrameRate(), 0f);
});
-
- assertTrue(drawLatch.await(1, TimeUnit.SECONDS));
- }
+ });
+ waitForAfterDraw();
}
@Test
@@ -211,8 +241,10 @@
mMovingView.invalidate();
int expected = toolkitFrameRateBySizeReadOnly()
? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL;
- assertEquals(expected, mViewRoot.getPreferredFrameRateCategory());
+ runAfterDraw(
+ () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
}
@Test
@@ -245,8 +277,10 @@
mMovingView.invalidate();
int expected = toolkitFrameRateBySizeReadOnly()
? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL;
- assertEquals(expected, mViewRoot.getPreferredFrameRateCategory());
+ runAfterDraw(
+ () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
}
@Test
@@ -279,8 +313,10 @@
mMovingView.invalidate();
int expected = toolkitFrameRateBySizeReadOnly()
? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL;
- assertEquals(expected, mViewRoot.getPreferredFrameRateCategory());
+ runAfterDraw(
+ () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
}
@Test
@@ -313,8 +349,10 @@
mMovingView.invalidate();
int expected = toolkitFrameRateDefaultNormalReadOnly()
? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
- assertEquals(expected, mViewRoot.getPreferredFrameRateCategory());
+ runAfterDraw(
+ () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
}
@Test
@@ -347,8 +385,10 @@
mMovingView.invalidate();
int expected = toolkitFrameRateDefaultNormalReadOnly()
? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
- assertEquals(expected, mViewRoot.getPreferredFrameRateCategory());
+ runAfterDraw(
+ () -> assertEquals(expected, mViewRoot.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
}
@Test
@@ -367,8 +407,74 @@
mMovingView.invalidate();
int expected = toolkitFrameRateDefaultNormalReadOnly()
? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
- assertEquals(expected,
- mViewRoot.getPreferredFrameRateCategory());
+ runAfterDraw(() -> assertEquals(expected,
+ mViewRoot.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
+ }
+
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY
+ })
+ public void frameRateAndCategory() throws Throwable {
+ waitForFrameRateCategoryToSettle();
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+ mMovingView.setFrameContentVelocity(1f);
+ mMovingView.invalidate();
+ runAfterDraw(() -> {
+ assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ assertEquals(60f, mViewRoot.getLastPreferredFrameRate());
+ });
+ });
+ waitForAfterDraw();
+ }
+
+ private void runAfterDraw(@NonNull Runnable runnable) {
+ Handler handler = new Handler(Looper.getMainLooper());
+ mAfterDrawLatch = new CountDownLatch(1);
+ ViewTreeObserver.OnDrawListener listener = new ViewTreeObserver.OnDrawListener() {
+ @Override
+ public void onDraw() {
+ handler.postAtFrontOfQueue(() -> {
+ mMovingView.getViewTreeObserver().removeOnDrawListener(this);
+ try {
+ runnable.run();
+ } catch (Throwable t) {
+ mAfterDrawThrowable = t;
+ }
+ mAfterDrawLatch.countDown();
+ });
+ }
+ };
+ mMovingView.getViewTreeObserver().addOnDrawListener(listener);
+ }
+
+ private void waitForAfterDraw() throws Throwable {
+ assertTrue(mAfterDrawLatch.await(1, TimeUnit.SECONDS));
+ if (mAfterDrawThrowable != null) {
+ throw mAfterDrawThrowable;
+ }
+ }
+
+ private void waitForFrameRateCategoryToSettle() throws Throwable {
+ for (int i = 0; i < 5 || mViewRoot.getIsFrameRateBoosting(); i++) {
+ final CountDownLatch drawLatch = new CountDownLatch(1);
+
+ // Now that it is small, any invalidation should have a normal category
+ ViewTreeObserver.OnDrawListener listener = drawLatch::countDown;
+
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.invalidate();
+ mMovingView.getViewTreeObserver().addOnDrawListener(listener);
+ });
+
+ assertTrue(drawLatch.await(1, TimeUnit.SECONDS));
+ mActivityRule.runOnUiThread(
+ () -> mMovingView.getViewTreeObserver().removeOnDrawListener(listener));
+ }
}
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 90ee36e..80fef6c 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -22,6 +22,7 @@
import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY;
import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+import static android.view.Surface.FRAME_RATE_CATEGORY_DEFAULT;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
@@ -43,17 +44,21 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly;
import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly;
+import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
+import android.annotation.NonNull;
import android.app.Instrumentation;
import android.app.UiModeManager;
import android.content.Context;
@@ -107,6 +112,7 @@
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private ViewRootImpl mViewRootImpl;
+ private View mView;
private volatile boolean mKeyReceived = false;
private static Context sContext;
@@ -116,6 +122,9 @@
// state after the test completes.
private static boolean sOriginalTouchMode;
+ private CountDownLatch mAfterDrawLatch;
+ private Throwable mAfterDrawThrowable;
+
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -148,6 +157,16 @@
setForceDarkSysProp(false);
});
+ if (mView != null) {
+ sInstrumentation.runOnMainSync(() -> {
+ WindowManager wm = sContext.getSystemService(WindowManager.class);
+ wm.removeView(mView);
+ });
+ mView = null;
+ }
+ mViewRootImpl = null;
+ mAfterDrawLatch = null;
+ mAfterDrawThrowable = null;
}
@Test
@@ -309,7 +328,7 @@
*/
@Test
public void requestScrollCapture_timeout() {
- final View view = new View(sContext);
+ View view = new View(sContext);
view.setScrollCaptureCallback(new TestScrollCaptureCallback()); // Does nothing
sInstrumentation.runOnMainSync(() -> {
WindowManager.LayoutParams wmlp =
@@ -337,9 +356,9 @@
@Test
public void whenTouchModeChanges_viewRootIsNotified() throws Exception {
- View view = new View(sContext);
- attachViewToWindow(view);
- ViewTreeObserver viewTreeObserver = view.getRootView().getViewTreeObserver();
+ mView = new View(sContext);
+ attachViewToWindow(mView);
+ ViewTreeObserver viewTreeObserver = mView.getRootView().getViewTreeObserver();
CountDownLatch latch = new CountDownLatch(1);
ViewTreeObserver.OnTouchModeChangeListener touchModeListener = (boolean inTouchMode) -> {
assertWithMessage("addOnTouchModeChangeListener parameter").that(
@@ -349,10 +368,10 @@
viewTreeObserver.addOnTouchModeChangeListener(touchModeListener);
try {
- view.requestFocusFromTouch();
+ mView.requestFocusFromTouch();
assertThat(latch.await(1, TimeUnit.SECONDS)).isTrue();
- assertThat(view.isInTouchMode()).isFalse();
+ assertThat(mView.isInTouchMode()).isFalse();
} finally {
viewTreeObserver.removeOnTouchModeChangeListener(touchModeListener);
}
@@ -360,25 +379,25 @@
@Test
public void whenDispatchFakeFocus_focusDoesNotPersist() throws Exception {
- View view = new View(sContext);
- attachViewToWindow(view);
- view.clearFocus();
+ mView = new View(sContext);
+ attachViewToWindow(mView);
+ mView.clearFocus();
- assertThat(view.hasWindowFocus()).isFalse();
+ assertThat(mView.hasWindowFocus()).isFalse();
- mViewRootImpl = view.getViewRootImpl();
+ mViewRootImpl = mView.getViewRootImpl();
mViewRootImpl.dispatchCompatFakeFocus();
- assertThat(view.hasWindowFocus()).isFalse();
+ assertThat(mView.hasWindowFocus()).isFalse();
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
public void whenViewIsAttachedToWindow_getHostToken() {
- View view = new View(sContext);
- attachViewToWindow(view);
+ mView = new View(sContext);
+ attachViewToWindow(mView);
- mViewRootImpl = view.getViewRootImpl();
+ mViewRootImpl = mView.getViewRootImpl();
assertThat(mViewRootImpl.getInputTransferToken()).isNotEqualTo(null);
}
@@ -478,13 +497,13 @@
@UiThreadTest
@Test
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
- FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY})
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_getDefaultValues() {
ViewRootImpl viewRootImpl = new ViewRootImpl(sContext,
sContext.getDisplayNoVerify());
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
- assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+ assertEquals(FRAME_RATE_CATEGORY_DEFAULT,
+ viewRootImpl.getLastPreferredFrameRateCategory());
+ assertEquals(0, viewRootImpl.getLastPreferredFrameRate(), 0.1);
}
/**
@@ -496,29 +515,32 @@
@Test
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY,
- FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY})
- public void votePreferredFrameRate_voteFrameRateCategory_visibility_bySize() {
- View view = new View(sContext);
- attachViewToWindow(view);
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
+ public void votePreferredFrameRate_voteFrameRateCategory_visibility_bySize() throws Throwable {
+ mView = new View(sContext);
+ attachViewToWindow(mView);
+ mViewRootImpl = mView.getViewRootImpl();
+ waitForFrameRateCategoryToSettle(mView);
+ ViewTreeObserver.OnDrawListener failIfDrawn = () -> fail("Should not draw invisible views");
sInstrumentation.runOnMainSync(() -> {
- view.setVisibility(View.INVISIBLE);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ mView.setVisibility(View.INVISIBLE);
+ mView.invalidate();
+ mView.getViewTreeObserver().addOnDrawListener(failIfDrawn);
});
sInstrumentation.waitForIdleSync();
+ mView.getViewTreeObserver().removeOnDrawListener(failIfDrawn);
+
+ sInstrumentation.runOnMainSync(() -> {
+ mView.setVisibility(View.VISIBLE);
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
+ });
+ waitForAfterDraw();
+ sInstrumentation.waitForIdleSync();
sInstrumentation.runOnMainSync(() -> {
- view.setVisibility(View.VISIBLE);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NORMAL);
- });
- sInstrumentation.waitForIdleSync();
-
- sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.getIsFrameRateBoosting(), true);
+ assertTrue(mViewRootImpl.getIsFrameRateBoosting());
});
}
@@ -530,9 +552,9 @@
@Test
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY,
- FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY})
- public void votePreferredFrameRate_voteFrameRateCategory_smallSize_bySize() {
- View view = new View(sContext);
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
+ public void votePreferredFrameRate_voteFrameRateCategory_smallSize_bySize() throws Throwable {
+ mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
wmlp.width = 1;
@@ -540,15 +562,18 @@
sInstrumentation.runOnMainSync(() -> {
WindowManager wm = sContext.getSystemService(WindowManager.class);
- wm.addView(view, wmlp);
+ wm.addView(mView, wmlp);
});
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ mViewRootImpl = mView.getViewRootImpl();
+ waitForFrameRateCategoryToSettle(mView);
sInstrumentation.runOnMainSync(() -> {
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
}
/**
@@ -559,9 +584,9 @@
@Test
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY,
- FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY})
- public void votePreferredFrameRate_voteFrameRateCategory_normalSize_bySize() {
- View view = new View(sContext);
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
+ public void votePreferredFrameRate_voteFrameRateCategory_normalSize_bySize() throws Throwable {
+ mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -572,15 +597,18 @@
display.getMetrics(metrics);
wmlp.width = (int) (metrics.widthPixels * 0.9);
wmlp.height = (int) (metrics.heightPixels * 0.9);
- wm.addView(view, wmlp);
+ wm.addView(mView, wmlp);
});
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ mViewRootImpl = mView.getViewRootImpl();
+ waitForFrameRateCategoryToSettle(mView);
sInstrumentation.runOnMainSync(() -> {
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
}
/**
@@ -593,30 +621,56 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
- public void votePreferredFrameRate_voteFrameRateCategory_visibility_defaultHigh() {
- View view = new View(sContext);
- attachViewToWindow(view);
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ public void votePreferredFrameRate_voteFrameRateCategory_visibility_defaultHigh()
+ throws Throwable {
+ mView = new View(sContext);
+ WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+ wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
+
sInstrumentation.runOnMainSync(() -> {
- view.setVisibility(View.INVISIBLE);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ DisplayMetrics metrics = sContext.getResources().getDisplayMetrics();
+ wmlp.width = metrics.widthPixels / 2;
+ wmlp.height = metrics.heightPixels / 2;
+ WindowManager wm = sContext.getSystemService(WindowManager.class);
+ wm.addView(mView, wmlp);
});
sInstrumentation.waitForIdleSync();
+ mViewRootImpl = mView.getViewRootImpl();
+ waitForFrameRateCategoryToSettle(mView);
+ ViewTreeObserver.OnDrawListener failIfDrawn = () -> fail("Draw was not expected!");
+ sInstrumentation.runOnMainSync(() -> {
+ mView.setVisibility(View.INVISIBLE);
+ mView.invalidate();
+ mView.getViewTreeObserver().addOnDrawListener(failIfDrawn);
+ });
+ sInstrumentation.waitForIdleSync();
+ sInstrumentation.runOnMainSync(
+ () -> mView.getViewTreeObserver().removeOnDrawListener(failIfDrawn));
+
+ sInstrumentation.runOnMainSync(() -> {
+ mView.setVisibility(View.VISIBLE);
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
+ });
+ waitForAfterDraw();
+ sInstrumentation.waitForIdleSync();
sInstrumentation.runOnMainSync(() -> {
- view.setVisibility(View.VISIBLE);
- view.invalidate();
+ assertTrue(mViewRootImpl.getIsFrameRateBoosting());
+ });
+
+ waitForFrameRateCategoryToSettle(mView);
+
+ sInstrumentation.runOnMainSync(() -> {
+ mView.setVisibility(View.VISIBLE);
+ mView.invalidate();
int expected = toolkitFrameRateDefaultNormalReadOnly()
? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
- assertEquals(expected, viewRootImpl.getPreferredFrameRateCategory());
+ runAfterDraw(() -> assertEquals(expected,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
});
- sInstrumentation.waitForIdleSync();
-
- sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.getIsFrameRateBoosting(), true);
- });
+ waitForAfterDraw();
}
/**
@@ -628,8 +682,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
- public void votePreferredFrameRate_voteFrameRateCategory_smallSize_defaultHigh() {
- View view = new View(sContext);
+ public void votePreferredFrameRate_voteFrameRateCategory_smallSize_defaultHigh()
+ throws Throwable {
+ mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
wmlp.width = 1;
@@ -637,15 +692,20 @@
sInstrumentation.runOnMainSync(() -> {
WindowManager wm = sContext.getSystemService(WindowManager.class);
- wm.addView(view, wmlp);
+ wm.addView(mView, wmlp);
});
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ mViewRootImpl = mView.getViewRootImpl();
+ waitForFrameRateCategoryToSettle(mView);
sInstrumentation.runOnMainSync(() -> {
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+ mView.invalidate();
+ int expected = toolkitFrameRateBySizeReadOnly() ? FRAME_RATE_CATEGORY_LOW
+ : FRAME_RATE_CATEGORY_NORMAL;
+ runAfterDraw(() -> assertEquals(expected,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
}
/**
@@ -657,8 +717,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
- public void votePreferredFrameRate_voteFrameRateCategory_normalSize_defaultHigh() {
- View view = new View(sContext);
+ public void votePreferredFrameRate_voteFrameRateCategory_normalSize_defaultHigh()
+ throws Throwable {
+ mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -669,17 +730,20 @@
display.getMetrics(metrics);
wmlp.width = (int) (metrics.widthPixels * 0.9);
wmlp.height = (int) (metrics.heightPixels * 0.9);
- wm.addView(view, wmlp);
+ wm.addView(mView, wmlp);
});
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ mViewRootImpl = mView.getViewRootImpl();
+ waitForFrameRateCategoryToSettle(mView);
sInstrumentation.runOnMainSync(() -> {
- view.invalidate();
+ mView.invalidate();
int expected = toolkitFrameRateDefaultNormalReadOnly()
? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
- assertEquals(expected, viewRootImpl.getPreferredFrameRateCategory());
+ runAfterDraw(() -> assertEquals(expected,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
}
/**
@@ -688,41 +752,41 @@
*/
@Test
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
- FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY})
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRateCategory_aggregate() {
- View view = new View(sContext);
- attachViewToWindow(view);
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ View mView1 = new View(sContext);
+ attachViewToWindow(mView1);
+ ViewRootImpl viewRootImpl = mView1.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ assertEquals(FRAME_RATE_CATEGORY_DEFAULT,
+ viewRootImpl.getPreferredFrameRateCategory());
});
// reset the frame rate category counts
for (int i = 0; i < 5; i++) {
sInstrumentation.runOnMainSync(() -> {
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
- view.invalidate();
+ mView1.setRequestedFrameRate(mView1.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ mView1.invalidate();
});
sInstrumentation.waitForIdleSync();
}
sInstrumentation.runOnMainSync(() -> {
viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW, 0, null);
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
+ assertEquals(FRAME_RATE_CATEGORY_LOW, viewRootImpl.getPreferredFrameRateCategory());
viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL, 0, null);
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+ assertEquals(FRAME_RATE_CATEGORY_NORMAL, viewRootImpl.getPreferredFrameRateCategory());
viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH_HINT, 0, null);
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_HIGH_HINT);
+ assertEquals(FRAME_RATE_CATEGORY_HIGH_HINT,
+ viewRootImpl.getPreferredFrameRateCategory());
viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH, 0, null);
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ assertEquals(FRAME_RATE_CATEGORY_HIGH, viewRootImpl.getPreferredFrameRateCategory());
viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH_HINT, 0, null);
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ assertEquals(FRAME_RATE_CATEGORY_HIGH, viewRootImpl.getPreferredFrameRateCategory());
viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL, 0, null);
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ assertEquals(FRAME_RATE_CATEGORY_HIGH, viewRootImpl.getPreferredFrameRateCategory());
viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW, 0, null);
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ assertEquals(FRAME_RATE_CATEGORY_HIGH, viewRootImpl.getPreferredFrameRateCategory());
});
}
@@ -734,55 +798,67 @@
*/
@Test
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
- FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY})
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRate_aggregate() {
- View view = new View(sContext);
- attachViewToWindow(view);
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ mView = new View(sContext);
+ attachViewToWindow(mView);
+ mViewRootImpl = mView.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
- assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ assertEquals(0, mViewRootImpl.getPreferredFrameRate(), 0.1);
+ assertEquals(mViewRootImpl.getFrameRateCompatibility(),
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
- assertEquals(viewRootImpl.isFrameRateConflicted(), false);
- viewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_GTE);
- assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
- assertEquals(viewRootImpl.getFrameRateCompatibility(),
- FRAME_RATE_COMPATIBILITY_GTE);
- assertEquals(viewRootImpl.isFrameRateConflicted(), false);
- viewRootImpl.votePreferredFrameRate(30, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
- assertEquals(viewRootImpl.getPreferredFrameRate(), 30, 0.1);
+ assertFalse(mViewRootImpl.isFrameRateConflicted());
+ mViewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_GTE);
+ if (toolkitFrameRateVelocityMappingReadOnly()) {
+ assertEquals(24, mViewRootImpl.getPreferredFrameRate(), 0.1);
+ assertEquals(FRAME_RATE_COMPATIBILITY_GTE,
+ mViewRootImpl.getFrameRateCompatibility());
+ assertFalse(mViewRootImpl.isFrameRateConflicted());
+ } else {
+ assertEquals(FRAME_RATE_CATEGORY_HIGH,
+ mViewRootImpl.getPreferredFrameRateCategory());
+ }
+ mViewRootImpl.votePreferredFrameRate(30, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(30, mViewRootImpl.getPreferredFrameRate(), 0.1);
// If there is a conflict, then set compatibility to
// FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
- assertEquals(viewRootImpl.getFrameRateCompatibility(),
- FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
- // Should be true since there is a conflict between 24 and 30.
- assertEquals(viewRootImpl.isFrameRateConflicted(), true);
- view.invalidate();
+ assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ mViewRootImpl.getFrameRateCompatibility());
+ if (toolkitFrameRateVelocityMappingReadOnly()) {
+ // Should be true since there is a conflict between 24 and 30.
+ assertTrue(mViewRootImpl.isFrameRateConflicted());
+ }
+
+ mView.invalidate();
});
sInstrumentation.waitForIdleSync();
sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.isFrameRateConflicted(), false);
- viewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
- assertEquals(viewRootImpl.getPreferredFrameRate(), 60, 0.1);
- assertEquals(viewRootImpl.getFrameRateCompatibility(),
- FRAME_RATE_COMPATIBILITY_GTE);
- assertEquals(viewRootImpl.isFrameRateConflicted(), false);
- viewRootImpl.votePreferredFrameRate(120, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
- assertEquals(viewRootImpl.getPreferredFrameRate(), 120, 0.1);
- assertEquals(viewRootImpl.getFrameRateCompatibility(),
- FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertFalse(mViewRootImpl.isFrameRateConflicted());
+ mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
+ if (toolkitFrameRateVelocityMappingReadOnly()) {
+ assertEquals(60, mViewRootImpl.getPreferredFrameRate(), 0.1);
+ assertEquals(FRAME_RATE_COMPATIBILITY_GTE,
+ mViewRootImpl.getFrameRateCompatibility());
+ } else {
+ assertEquals(FRAME_RATE_CATEGORY_HIGH,
+ mViewRootImpl.getPreferredFrameRateCategory());
+ }
+ assertFalse(mViewRootImpl.isFrameRateConflicted());
+ mViewRootImpl.votePreferredFrameRate(120, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(120, mViewRootImpl.getPreferredFrameRate(), 0.1);
+ assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ mViewRootImpl.getFrameRateCompatibility());
// Should be false since 60 is a divisor of 120.
- assertEquals(viewRootImpl.isFrameRateConflicted(), false);
- viewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
- assertEquals(viewRootImpl.getPreferredFrameRate(), 120, 0.1);
+ assertFalse(mViewRootImpl.isFrameRateConflicted());
+ mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
+ assertEquals(120, mViewRootImpl.getPreferredFrameRate(), 0.1);
// compatibility should be remained the same (FRAME_RATE_COMPATIBILITY_FIXED_SOURCE)
// since the frame rate 60 is smaller than 120.
- assertEquals(viewRootImpl.getFrameRateCompatibility(),
- FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ mViewRootImpl.getFrameRateCompatibility());
// Should be false since 60 is a divisor of 120.
- assertEquals(viewRootImpl.isFrameRateConflicted(), false);
-
+ assertFalse(mViewRootImpl.isFrameRateConflicted());
});
}
@@ -795,37 +871,49 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
- public void votePreferredFrameRate_voteFrameRate_category() {
- View view = new View(sContext);
- attachViewToWindow(view);
+ public void votePreferredFrameRate_voteFrameRate_category() throws Throwable {
+ mView = new View(sContext);
+ attachViewToWindow(mView);
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ mViewRootImpl = mView.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ assertEquals(FRAME_RATE_CATEGORY_DEFAULT,
+ mViewRootImpl.getPreferredFrameRateCategory());
});
// reset the frame rate category counts
for (int i = 0; i < 5; i++) {
sInstrumentation.runOnMainSync(() -> {
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
- view.invalidate();
+ mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ mView.invalidate();
});
sInstrumentation.waitForIdleSync();
}
+ waitForFrameRateCategoryToSettle(mView);
+
sInstrumentation.runOnMainSync(() -> {
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_LOW);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
+ sInstrumentation.runOnMainSync(() -> {
+ mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
+ });
+ waitForAfterDraw();
+ sInstrumentation.runOnMainSync(() -> {
+ mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
+ });
+ waitForAfterDraw();
}
/**
@@ -837,8 +925,8 @@
FLAG_VIEW_VELOCITY_API,
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
- public void votePreferredFrameRate_voteFrameRateCategory_velocityToHigh() {
- View view = new View(sContext);
+ public void votePreferredFrameRate_voteFrameRateCategory_velocityToHigh() throws Throwable {
+ mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
wmlp.width = 1;
@@ -846,21 +934,31 @@
sInstrumentation.runOnMainSync(() -> {
WindowManager wm = sContext.getSystemService(WindowManager.class);
- wm.addView(view, wmlp);
+ wm.addView(mView, wmlp);
});
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ mViewRootImpl = mView.getViewRootImpl();
+ waitForFrameRateCategoryToSettle(mView);
sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
- view.setFrameContentVelocity(100);
- view.invalidate();
- assertTrue(viewRootImpl.getPreferredFrameRate() > 0);
+ assertEquals(0, mViewRootImpl.getPreferredFrameRate(), 0.1);
+ mView.setFrameContentVelocity(100);
+ mView.invalidate();
+ runAfterDraw(() -> {
+ if (toolkitFrameRateVelocityMappingReadOnly()) {
+ assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRootImpl.getLastPreferredFrameRateCategory());
+ assertTrue(mViewRootImpl.getLastPreferredFrameRate() >= 60f);
+ } else {
+ assertEquals(FRAME_RATE_CATEGORY_HIGH,
+ mViewRootImpl.getLastPreferredFrameRateCategory());
+ assertEquals(0, mViewRootImpl.getLastPreferredFrameRate(), 0.1);
+ }
+ });
});
+ waitForAfterDraw();
sInstrumentation.waitForIdleSync();
- assertEquals(viewRootImpl.getLastPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
- assertEquals(viewRootImpl.getLastPreferredFrameRate(), 0 , 0.1);
}
/**
@@ -868,9 +966,9 @@
*/
@Test
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
- FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY})
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_insetsAnimation() {
- View view = new View(sContext);
+ mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -881,21 +979,21 @@
display.getMetrics(metrics);
wmlp.width = (int) (metrics.widthPixels * 0.9);
wmlp.height = (int) (metrics.heightPixels * 0.9);
- wm.addView(view, wmlp);
+ wm.addView(mView, wmlp);
});
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ ViewRootImpl viewRootImpl = mView.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
- view.invalidate();
+ mView.invalidate();
viewRootImpl.notifyInsetsAnimationRunningStateChanged(true);
- view.invalidate();
+ mView.invalidate();
});
sInstrumentation.waitForIdleSync();
sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.getLastPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_HIGH);
+ assertEquals(FRAME_RATE_CATEGORY_HIGH,
+ viewRootImpl.getLastPreferredFrameRateCategory());
});
}
@@ -905,17 +1003,17 @@
*/
@Test
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
- FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY})
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_frameRateBoostOnTouch() {
- View view = new View(sContext);
- attachViewToWindow(view);
+ mView = new View(sContext);
+ attachViewToWindow(mView);
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ ViewRootImpl viewRootImpl = mView.getViewRootImpl();
final WindowManager.LayoutParams attrs = viewRootImpl.mWindowAttributes;
- assertEquals(attrs.getFrameRateBoostOnTouchEnabled(), true);
- assertEquals(viewRootImpl.getFrameRateBoostOnTouchEnabled(),
- attrs.getFrameRateBoostOnTouchEnabled());
+ assertTrue(attrs.getFrameRateBoostOnTouchEnabled());
+ assertEquals(attrs.getFrameRateBoostOnTouchEnabled(),
+ viewRootImpl.getFrameRateBoostOnTouchEnabled());
sInstrumentation.runOnMainSync(() -> {
attrs.setFrameRateBoostOnTouchEnabled(false);
@@ -925,9 +1023,9 @@
sInstrumentation.runOnMainSync(() -> {
final WindowManager.LayoutParams newAttrs = viewRootImpl.mWindowAttributes;
- assertEquals(newAttrs.getFrameRateBoostOnTouchEnabled(), false);
- assertEquals(viewRootImpl.getFrameRateBoostOnTouchEnabled(),
- newAttrs.getFrameRateBoostOnTouchEnabled());
+ assertFalse(newAttrs.getFrameRateBoostOnTouchEnabled());
+ assertEquals(newAttrs.getFrameRateBoostOnTouchEnabled(),
+ viewRootImpl.getFrameRateBoostOnTouchEnabled());
});
}
@@ -938,37 +1036,37 @@
*/
@Test
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
- FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY})
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRateTimeOut() throws InterruptedException {
final long delay = 200L;
- View view = new View(sContext);
- attachViewToWindow(view);
+ mView = new View(sContext);
+ attachViewToWindow(mView);
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ ViewRootImpl viewRootImpl = mView.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
- assertEquals(viewRootImpl.getFrameRateCompatibility(),
- FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
- assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ assertEquals(0, viewRootImpl.getPreferredFrameRate(), 0.1);
+ assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ viewRootImpl.getFrameRateCompatibility());
+ assertFalse(viewRootImpl.isFrameRateConflicted());
viewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
- assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
- assertEquals(viewRootImpl.getFrameRateCompatibility(),
- FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
- assertEquals(viewRootImpl.isFrameRateConflicted(), false);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
- assertEquals(viewRootImpl.getFrameRateCompatibility(),
- FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
- assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ assertEquals(24, viewRootImpl.getPreferredFrameRate(), 0.1);
+ assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ viewRootImpl.getFrameRateCompatibility());
+ assertFalse(viewRootImpl.isFrameRateConflicted());
+ mView.invalidate();
+ assertEquals(24, viewRootImpl.getPreferredFrameRate(), 0.1);
+ assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ viewRootImpl.getFrameRateCompatibility());
+ assertFalse(viewRootImpl.isFrameRateConflicted());
});
Thread.sleep(delay);
- assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
- assertEquals(viewRootImpl.getFrameRateCompatibility(),
- FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
- assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ assertEquals(0, viewRootImpl.getPreferredFrameRate(), 0.1);
+ assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ viewRootImpl.getFrameRateCompatibility());
+ assertFalse(viewRootImpl.isFrameRateConflicted());
}
/**
@@ -978,38 +1076,45 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
- public void votePreferredFrameRate_voteFrameRateOnly() {
- View view = new View(sContext);
+ public void votePreferredFrameRate_voteFrameRateOnly() throws Throwable {
+ mView = new View(sContext);
float frameRate = 20;
- attachViewToWindow(view);
+ attachViewToWindow(mView);
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ mViewRootImpl = mView.getViewRootImpl();
+ waitForFrameRateCategoryToSettle(mView);
sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ assertEquals(FRAME_RATE_CATEGORY_DEFAULT,
+ mViewRootImpl.getPreferredFrameRateCategory());
- view.setRequestedFrameRate(frameRate);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
- assertEquals(viewRootImpl.getPreferredFrameRate(), frameRate, 0.1);
+ mView.setRequestedFrameRate(frameRate);
+ mView.invalidate();
+ runAfterDraw(() -> {
+ int expected = toolkitFrameRateDefaultNormalReadOnly()
+ ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
+ assertEquals(expected, mViewRootImpl.getLastPreferredFrameRateCategory());
+ assertEquals(frameRate, mViewRootImpl.getLastPreferredFrameRate(), 0.1);
+ });
});
+ waitForAfterDraw();
// reset the frame rate category counts
for (int i = 0; i < 5; i++) {
sInstrumentation.runOnMainSync(() -> {
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
- view.invalidate();
+ mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ mView.invalidate();
});
sInstrumentation.waitForIdleSync();
}
sInstrumentation.runOnMainSync(() -> {
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_LOW);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
+ mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
}
/**
@@ -1022,10 +1127,10 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
- public void votePreferredFrameRate_infrequentLayer_defaultHigh() throws InterruptedException {
+ public void votePreferredFrameRate_infrequentLayer_defaultHigh() throws Throwable {
final long delay = 200L;
- View view = new View(sContext);
+ mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -1036,26 +1141,29 @@
display.getMetrics(metrics);
wmlp.width = (int) (metrics.widthPixels * 0.9);
wmlp.height = (int) (metrics.heightPixels * 0.9);
- wm.addView(view, wmlp);
+ wm.addView(mView, wmlp);
});
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ mViewRootImpl = mView.getViewRootImpl();
+ waitForFrameRateCategoryToSettle(mView);
// In transition from frequent update to infrequent update
Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
- view.invalidate();
+ mView.invalidate();
int expected = toolkitFrameRateDefaultNormalReadOnly()
? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
- assertEquals(expected, viewRootImpl.getPreferredFrameRateCategory());
+ runAfterDraw(() -> assertEquals(expected,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
// reset the frame rate category counts
for (int i = 0; i < 5; i++) {
sInstrumentation.runOnMainSync(() -> {
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
- view.invalidate();
+ mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ mView.invalidate();
});
sInstrumentation.waitForIdleSync();
}
@@ -1063,26 +1171,32 @@
// In transition from frequent update to infrequent update
Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ mView.invalidate();
+ runAfterDraw(() -> {
+ assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+ mViewRootImpl.getLastPreferredFrameRateCategory());
+ });
});
+ waitForAfterDraw();
Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
// Infrequent update
Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+ mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
}
/**
@@ -1090,17 +1204,17 @@
*/
@Test
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
- FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY})
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_isFrameRatePowerSavingsBalanced() {
- View view = new View(sContext);
- attachViewToWindow(view);
+ mView = new View(sContext);
+ attachViewToWindow(mView);
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRoot = view.getViewRootImpl();
+ ViewRootImpl viewRoot = mView.getViewRootImpl();
final WindowManager.LayoutParams attrs = viewRoot.mWindowAttributes;
- assertEquals(attrs.isFrameRatePowerSavingsBalanced(), true);
- assertEquals(viewRoot.isFrameRatePowerSavingsBalanced(),
- attrs.isFrameRatePowerSavingsBalanced());
+ assertTrue(attrs.isFrameRatePowerSavingsBalanced());
+ assertEquals(attrs.isFrameRatePowerSavingsBalanced(),
+ viewRoot.isFrameRatePowerSavingsBalanced());
sInstrumentation.runOnMainSync(() -> {
attrs.setFrameRatePowerSavingsBalanced(false);
@@ -1110,9 +1224,9 @@
sInstrumentation.runOnMainSync(() -> {
final WindowManager.LayoutParams newAttrs = viewRoot.mWindowAttributes;
- assertEquals(newAttrs.isFrameRatePowerSavingsBalanced(), false);
- assertEquals(viewRoot.isFrameRatePowerSavingsBalanced(),
- newAttrs.isFrameRatePowerSavingsBalanced());
+ assertFalse(newAttrs.isFrameRatePowerSavingsBalanced());
+ assertEquals(newAttrs.isFrameRatePowerSavingsBalanced(),
+ viewRoot.isFrameRatePowerSavingsBalanced());
});
}
@@ -1125,10 +1239,10 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
- public void votePreferredFrameRate_applyTextureViewHeuristic() throws InterruptedException {
+ public void votePreferredFrameRate_applyTextureViewHeuristic() throws Throwable {
final long delay = 30L;
- TextureView view = new TextureView(sContext);
+ mView = new TextureView(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -1139,38 +1253,44 @@
display.getMetrics(metrics);
wmlp.width = (int) (metrics.widthPixels * 0.9);
wmlp.height = (int) (metrics.heightPixels * 0.9);
- wm.addView(view, wmlp);
+ wm.addView(mView, wmlp);
});
sInstrumentation.waitForIdleSync();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ mViewRootImpl = mView.getViewRootImpl();
+ waitForFrameRateCategoryToSettle(mView);
sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
- view.invalidate();
+ assertEquals(FRAME_RATE_CATEGORY_DEFAULT,
+ mViewRootImpl.getPreferredFrameRateCategory());
+ mView.invalidate();
int expected = toolkitFrameRateDefaultNormalReadOnly()
? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
- assertEquals(expected, viewRootImpl.getPreferredFrameRateCategory());
+ runAfterDraw(() -> assertEquals(expected,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
+
+ waitForFrameRateCategoryToSettle(mView);
// reset the frame rate category counts
for (int i = 0; i < 5; i++) {
Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
- view.invalidate();
+ mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ mView.invalidate();
});
sInstrumentation.waitForIdleSync();
}
Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
- view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NORMAL);
+ mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
});
+ waitForAfterDraw();
}
@Test
@@ -1287,6 +1407,7 @@
*/
private void checkKeyEvent(Runnable setup, boolean shouldReceiveKey) {
final KeyView view = new KeyView(sContext);
+ mView = view;
attachViewToWindow(view);
@@ -1300,7 +1421,7 @@
mViewRootImpl.dispatchInputEvent(event);
});
sInstrumentation.waitForIdleSync();
- assertEquals(mKeyReceived, shouldReceiveKey);
+ assertEquals(shouldReceiveKey, mKeyReceived);
}
private void attachViewToWindow(View view) {
@@ -1313,4 +1434,48 @@
});
sInstrumentation.waitForIdleSync();
}
+
+ private void runAfterDraw(@NonNull Runnable runnable) {
+ mAfterDrawLatch = new CountDownLatch(1);
+ ViewTreeObserver.OnDrawListener listener = new ViewTreeObserver.OnDrawListener() {
+ @Override
+ public void onDraw() {
+ mView.getHandler().postAtFrontOfQueue(() -> {
+ mView.getViewTreeObserver().removeOnDrawListener(this);
+ try {
+ runnable.run();
+ } catch (Throwable t) {
+ mAfterDrawThrowable = t;
+ }
+ mAfterDrawLatch.countDown();
+ });
+ }
+ };
+ mView.getViewTreeObserver().addOnDrawListener(listener);
+ }
+
+ private void waitForAfterDraw() throws Throwable {
+ assertTrue(mAfterDrawLatch.await(1, TimeUnit.SECONDS));
+ if (mAfterDrawThrowable != null) {
+ throw mAfterDrawThrowable;
+ }
+ }
+
+ private void waitForFrameRateCategoryToSettle(View view) throws Throwable {
+ for (int i = 0; i < 5 || mViewRootImpl.getIsFrameRateBoosting(); i++) {
+ final CountDownLatch drawLatch = new CountDownLatch(1);
+
+ // Now that it is small, any invalidation should have a normal category
+ ViewTreeObserver.OnDrawListener listener = drawLatch::countDown;
+
+ sInstrumentation.runOnMainSync(() -> {
+ view.invalidate();
+ view.getViewTreeObserver().addOnDrawListener(listener);
+ });
+
+ assertTrue(drawLatch.await(1, TimeUnit.SECONDS));
+ sInstrumentation.runOnMainSync(
+ () -> view.getViewTreeObserver().removeOnDrawListener(listener));
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 5fab1a0..d560ef2 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -420,7 +420,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void testClickingDisableButtonInDialog_shouldClearShortcutId() throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
configureValidShortcutService();
@@ -443,7 +443,7 @@
}
@Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void testClickingDisableButtonInDialog_shouldClearShortcutId_old() throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
configureValidShortcutService();
@@ -467,7 +467,7 @@
@Test
@EnableFlags(Flags.FLAG_UPDATE_ALWAYS_ON_A11Y_SERVICE)
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void turnOffVolumeShortcutForAlwaysOnA11yService_shouldTurnOffA11yService()
throws Exception {
configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
@@ -480,7 +480,7 @@
@Test
@EnableFlags(Flags.FLAG_UPDATE_ALWAYS_ON_A11Y_SERVICE)
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void turnOffVolumeShortcutForAlwaysOnA11yService_hasOtherTypesShortcut_shouldNotTurnOffA11yService()
throws Exception {
configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
@@ -527,7 +527,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void testTurnOnDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOn()
throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
@@ -551,7 +551,7 @@
}
@Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void testTurnOnDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOn_old()
throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
@@ -574,7 +574,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void testTurnOffDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOff()
throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
@@ -598,7 +598,7 @@
}
@Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void testTurnOffDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOff_old()
throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java
index a14d8e0..9cac312 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java
@@ -117,7 +117,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void onCheckedChanged_true_callA11yManagerToUpdateShortcuts() throws Exception {
mSut.onCheckedChanged(true);
@@ -130,7 +130,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void onCheckedChanged_false_callA11yManagerToUpdateShortcuts() throws Exception {
mSut.onCheckedChanged(false);
verify(mAccessibilityManagerService).enableShortcutsForTargets(
@@ -142,7 +142,7 @@
}
@Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void onCheckedChanged_turnOnShortcut_hasOtherShortcut_serviceKeepsOn() {
enableA11yService(/* enable= */ true);
addShortcutForA11yService(
@@ -155,7 +155,7 @@
}
@Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void onCheckedChanged_turnOnShortcut_noOtherShortcut_shouldTurnOnService() {
enableA11yService(/* enable= */ false);
addShortcutForA11yService(
@@ -168,7 +168,7 @@
}
@Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void onCheckedChanged_turnOffShortcut_hasOtherShortcut_serviceKeepsOn() {
enableA11yService(/* enable= */ true);
addShortcutForA11yService(
@@ -181,7 +181,7 @@
}
@Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void onCheckedChanged_turnOffShortcut_noOtherShortcut_shouldTurnOffService() {
enableA11yService(/* enable= */ true);
addShortcutForA11yService(
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index 533b799..fa5d72a 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -55,6 +55,21 @@
}
@Test
+ public void copyStatesFrom() {
+ LongArrayMultiStateCounter source = new LongArrayMultiStateCounter(2, 1);
+ updateValue(source, new long[]{0}, 1000);
+ source.setState(0, 1000);
+ source.setState(1, 2000);
+
+ LongArrayMultiStateCounter target = new LongArrayMultiStateCounter(2, 1);
+ target.copyStatesFrom(source);
+ updateValue(target, new long[]{1000}, 5000);
+
+ assertCounts(target, 0, new long[]{250});
+ assertCounts(target, 1, new long[]{750});
+ }
+
+ @Test
public void setValue() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index c0f0714..951fa98 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -21,20 +21,21 @@
import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
-import static org.junit.Assert.fail;
+import static com.google.common.truth.Truth.assertThat;
-import android.annotation.XmlRes;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Xml;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.frameworks.coretests.R;
import com.android.internal.power.ModemPowerProfile;
import com.android.internal.util.XmlUtils;
@@ -43,6 +44,11 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.StringReader;
/*
* Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml and
@@ -53,7 +59,6 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = PowerProfile.class)
public class PowerProfileTest {
@Rule
public final RavenwoodRule mRavenwood = new RavenwoodRule();
@@ -62,17 +67,15 @@
static final String ATTR_NAME = "name";
private PowerProfile mProfile;
- private Context mContext;
@Before
public void setUp() {
- mContext = InstrumentationRegistry.getContext();
- mProfile = new PowerProfile(mContext);
+ mProfile = new PowerProfile();
}
@Test
public void testPowerProfile() {
- mProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mProfile.initForTesting(resolveParser("power_profile_test"));
assertEquals(5.0, mProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND));
assertEquals(1.11, mProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE));
@@ -127,11 +130,36 @@
PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT
| ModemPowerProfile.MODEM_DRAIN_TYPE_TX
| ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ assertEquals(0.02, mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE));
+ assertEquals(3, mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX));
+ assertEquals(5, mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX));
+ assertEquals(3300, mProfile.getAveragePower(
+ PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE));
}
+
+ @DisabledOnRavenwood
+ @Test
+ public void configDefaults() throws XmlPullParserException {
+ Resources mockResources = mock(Resources.class);
+ when(mockResources.getInteger(com.android.internal.R.integer.config_bluetooth_rx_cur_ma))
+ .thenReturn(123);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new StringReader(
+ "<device name='Android'>"
+ + "<item name='bluetooth.controller.idle'>10</item>"
+ + "</device>"));
+ mProfile.initForTesting(parser, mockResources);
+ assertThat(mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE))
+ .isEqualTo(10);
+ assertThat(mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX))
+ .isEqualTo(123);
+ }
+
@Test
public void testPowerProfile_legacyCpuConfig() {
// This power profile has per-cluster data, rather than per-policy
- mProfile.forceInitForTesting(mContext, R.xml.power_profile_test_cpu_legacy);
+ mProfile.initForTesting(resolveParser("power_profile_test_cpu_legacy"));
assertEquals(2.11, mProfile.getAveragePowerForCpuScalingPolicy(0));
assertEquals(2.22, mProfile.getAveragePowerForCpuScalingPolicy(4));
@@ -148,7 +176,7 @@
@Test
public void testModemPowerProfile_defaultRat() throws Exception {
- final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem,
+ final XmlPullParser parser = getTestModemElement("power_profile_test_modem",
"testModemPowerProfile_defaultRat");
ModemPowerProfile mpp = new ModemPowerProfile();
mpp.parseFromXml(parser);
@@ -216,7 +244,7 @@
@Test
public void testModemPowerProfile_partiallyDefined() throws Exception {
- final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem,
+ final XmlPullParser parser = getTestModemElement("power_profile_test_modem",
"testModemPowerProfile_partiallyDefined");
ModemPowerProfile mpp = new ModemPowerProfile();
mpp.parseFromXml(parser);
@@ -369,7 +397,7 @@
@Test
public void testModemPowerProfile_fullyDefined() throws Exception {
- final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem,
+ final XmlPullParser parser = getTestModemElement("power_profile_test_modem",
"testModemPowerProfile_fullyDefined");
ModemPowerProfile mpp = new ModemPowerProfile();
mpp.parseFromXml(parser);
@@ -519,11 +547,10 @@
| ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
}
- private XmlResourceParser getTestModemElement(@XmlRes int xmlId, String elementName)
+ private XmlPullParser getTestModemElement(String resourceName, String elementName)
throws Exception {
+ XmlPullParser parser = resolveParser(resourceName);
final String element = TAG_TEST_MODEM;
- final Resources resources = mContext.getResources();
- XmlResourceParser parser = resources.getXml(xmlId);
while (true) {
XmlUtils.nextElement(parser);
final String e = parser.getName();
@@ -535,10 +562,26 @@
return parser;
}
- fail("Unanable to find element " + element + " with name " + elementName);
+ fail("Unable to find element " + element + " with name " + elementName);
return null;
}
+ private XmlPullParser resolveParser(String resourceName) {
+ if (RavenwoodRule.isOnRavenwood()) {
+ try {
+ return Xml.resolvePullParser(getClass().getClassLoader()
+ .getResourceAsStream("res/xml/" + resourceName + ".xml"));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ Context context = androidx.test.InstrumentationRegistry.getContext();
+ Resources resources = context.getResources();
+ int resId = resources.getIdentifier(resourceName, "xml", context.getPackageName());
+ return resources.getXml(resId);
+ }
+ }
+
private void assertEquals(double expected, double actual) {
Assert.assertEquals(expected, actual, 0.1);
}
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
index b99e202..6402206 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
@@ -21,20 +21,27 @@
import android.os.BatteryConsumer;
import android.os.Parcel;
import android.os.PersistableBundle;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.SparseArray;
+import android.util.Xml;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class PowerStatsTest {
@Rule
public final RavenwoodRule mRavenwood = new RavenwoodRule();
@@ -47,7 +54,10 @@
mRegistry = new PowerStats.DescriptorRegistry();
PersistableBundle extras = new PersistableBundle();
extras.putBoolean("hasPowerMonitor", true);
- mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, 2, extras);
+ SparseArray<String> stateLabels = new SparseArray<>();
+ stateLabels.put(0x0F, "idle");
+ mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, stateLabels,
+ 1, 2, extras);
mRegistry.register(mDescriptor);
}
@@ -58,6 +68,8 @@
stats.stats[0] = 10;
stats.stats[1] = 20;
stats.stats[2] = 30;
+ stats.stateStats.put(0x0F, new long[]{16});
+ stats.stateStats.put(0xF0, new long[]{17});
stats.uidStats.put(42, new long[]{40, 50});
stats.uidStats.put(99, new long[]{60, 70});
@@ -73,6 +85,7 @@
assertThat(newDescriptor.powerComponentId).isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU);
assertThat(newDescriptor.name).isEqualTo("cpu");
assertThat(newDescriptor.statsArrayLength).isEqualTo(3);
+ assertThat(newDescriptor.stateStatsArrayLength).isEqualTo(1);
assertThat(newDescriptor.uidStatsArrayLength).isEqualTo(2);
assertThat(newDescriptor.extras.getBoolean("hasPowerMonitor")).isTrue();
@@ -81,6 +94,11 @@
PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry);
assertThat(newStats.durationMs).isEqualTo(1234);
assertThat(newStats.stats).isEqualTo(new long[]{10, 20, 30});
+ assertThat(newStats.stateStats.size()).isEqualTo(2);
+ assertThat(newStats.stateStats.get(0x0F)).isEqualTo(new long[]{16});
+ assertThat(newStats.descriptor.getStateLabel(0x0F)).isEqualTo("idle");
+ assertThat(newStats.stateStats.get(0xF0)).isEqualTo(new long[]{17});
+ assertThat(newStats.descriptor.getStateLabel(0xF0)).isEqualTo("cpu-f0");
assertThat(newStats.uidStats.size()).isEqualTo(2);
assertThat(newStats.uidStats.get(42)).isEqualTo(new long[]{40, 50});
assertThat(newStats.uidStats.get(99)).isEqualTo(new long[]{60, 70});
@@ -90,9 +108,33 @@
}
@Test
+ public void xmlFormat() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ mDescriptor.writeXml(serializer);
+ serializer.flush();
+
+ byte[] bytes = out.toByteArray();
+
+ TypedXmlPullParser parser = Xml.newBinaryPullParser();
+ parser.setInput(new ByteArrayInputStream(bytes), StandardCharsets.UTF_8.name());
+ PowerStats.Descriptor actual = PowerStats.Descriptor.createFromXml(parser);
+
+ assertThat(actual.powerComponentId).isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU);
+ assertThat(actual.name).isEqualTo("cpu");
+ assertThat(actual.statsArrayLength).isEqualTo(3);
+ assertThat(actual.stateStatsArrayLength).isEqualTo(1);
+ assertThat(actual.getStateLabel(0x0F)).isEqualTo("idle");
+ assertThat(actual.getStateLabel(0xF0)).isEqualTo("cpu-f0");
+ assertThat(actual.uidStatsArrayLength).isEqualTo(2);
+ assertThat(actual.extras.getBoolean("hasPowerMonitor")).isEqualTo(true);
+ }
+
+ @Test
public void parceling_unrecognizedPowerComponent() {
PowerStats stats = new PowerStats(
- new PowerStats.Descriptor(777, "luck", 3, 2, new PersistableBundle()));
+ new PowerStats.Descriptor(777, "luck", 3, null, 1, 2, new PersistableBundle()));
stats.durationMs = 1234;
Parcel parcel = Parcel.obtain();
diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp
index 2d778b1..aca52a8 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -40,6 +40,7 @@
"platform-test-annotations",
"truth",
"testables",
+ "flag-junit",
],
libs: [
diff --git a/core/tests/mockingcoretests/src/android/widget/OWNERS b/core/tests/mockingcoretests/src/android/widget/OWNERS
new file mode 100644
index 0000000..c0cbea9
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/widget/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/notification/OWNERS
\ No newline at end of file
diff --git a/core/tests/mockingcoretests/src/android/widget/ToastTest.java b/core/tests/mockingcoretests/src/android/widget/ToastTest.java
new file mode 100644
index 0000000..79bc81d
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/widget/ToastTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.when;
+
+import android.app.INotificationManager;
+import android.content.Context;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.View;
+import android.widget.flags.Flags;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+
+/**
+ * ToastTest tests {@link Toast}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ToastTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private Context mContext;
+ private MockitoSession mMockingSession;
+ private static INotificationManager.Stub sMockNMS;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getContext();
+ mMockingSession =
+ ExtendedMockito.mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .mockStatic(ServiceManager.class)
+ .startMocking();
+
+ //Toast caches the NotificationManager service as static class member
+ if (sMockNMS == null) {
+ sMockNMS = mock(INotificationManager.Stub.class);
+ }
+ doReturn(sMockNMS).when(sMockNMS).queryLocalInterface("android.app.INotificationManager");
+ doReturn(sMockNMS).when(() -> ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ reset(sMockNMS);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_TOAST_NO_WEAKREF)
+ public void enqueueFail_nullifiesNextView() throws RemoteException {
+ Looper.prepare();
+
+ // allow 1st toast and fail on the 2nd
+ when(sMockNMS.enqueueToast(anyString(), any(), any(), anyInt(), anyBoolean(),
+ anyInt())).thenReturn(true, false);
+
+ // first toast is enqueued
+ Toast t = Toast.makeText(mContext, "Toast1", Toast.LENGTH_SHORT);
+ t.setView(mock(View.class));
+ t.show();
+ Toast.TN tn = t.getTn();
+ assertThat(tn.getNextView()).isNotNull();
+
+ // second toast is not enqueued
+ t = Toast.makeText(mContext, "Toast2", Toast.LENGTH_SHORT);
+ t.setView(mock(View.class));
+ t.show();
+ tn = t.getTn();
+ assertThat(tn.getNextView()).isNull();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_TOAST_NO_WEAKREF)
+ public void enqueueFail_doesNotNullifyNextView() throws RemoteException {
+ Looper.prepare();
+
+ // allow 1st toast and fail on the 2nd
+ when(sMockNMS.enqueueToast(anyString(), any(), any(), anyInt(), anyBoolean(),
+ anyInt())).thenReturn(true, false);
+
+ // first toast is enqueued
+ Toast t = Toast.makeText(mContext, "Toast1", Toast.LENGTH_SHORT);
+ t.setView(mock(View.class));
+ t.show();
+ Toast.TN tn = t.getTn();
+ assertThat(tn.getNextView()).isNotNull();
+
+ // second toast is not enqueued
+ t = Toast.makeText(mContext, "Toast2", Toast.LENGTH_SHORT);
+ t.setView(mock(View.class));
+ t.show();
+ tn = t.getTn();
+ assertThat(tn.getNextView()).isNotNull();
+ }
+}
diff --git a/core/tests/overlaytests/device_non_system/Android.bp b/core/tests/overlaytests/device_non_system/Android.bp
new file mode 100644
index 0000000..dd7786a
--- /dev/null
+++ b/core/tests/overlaytests/device_non_system/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "OverlayDeviceTestsNonSystem",
+ team: "trendy_team_android_resources",
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+ static_libs: [
+ "androidx.test.rules",
+ "testng",
+ "compatibility-device-util-axt",
+ ],
+ test_suites: ["device-tests"],
+ data: [
+ ":OverlayDeviceTestsNonSystem_AppOverlay",
+ ],
+}
diff --git a/core/tests/overlaytests/device_non_system/AndroidManifest.xml b/core/tests/overlaytests/device_non_system/AndroidManifest.xml
new file mode 100644
index 0000000..a37d168
--- /dev/null
+++ b/core/tests/overlaytests/device_non_system/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.non_system">
+
+ <uses-sdk android:minSdkVersion="34" />
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.overlaytest.non_system"
+ android:label="Runtime resource overlay tests for non system app" />
+</manifest>
diff --git a/core/tests/overlaytests/device_non_system/AndroidTest.xml b/core/tests/overlaytests/device_non_system/AndroidTest.xml
new file mode 100644
index 0000000..fc47e6a
--- /dev/null
+++ b/core/tests/overlaytests/device_non_system/AndroidTest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<configuration description="Test module config for OverlayDeviceTestsNonSystem">
+ <option name="test-tag" value="OverlayDeviceTestsNonSystem" />
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="OverlayDeviceTestsNonSystem_AppOverlay.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunOnSecondaryUserTargetPreparer">
+ <option name="test-package-name" value="com.android.overlaytest.non_system" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command"
+ value="cmd overlay enable --user %TEST_USER% com.android.overlaytest.non_system.app_overlay" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="OverlayDeviceTestsNonSystem.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.overlaytest.non_system" />
+ </test>
+</configuration>
diff --git a/core/tests/overlaytests/device_non_system/res/layout/layout.xml b/core/tests/overlaytests/device_non_system/res/layout/layout.xml
new file mode 100644
index 0000000..2cb2013
--- /dev/null
+++ b/core/tests/overlaytests/device_non_system/res/layout/layout.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/text_view_id"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/test_string" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_biometric_icon.xml b/core/tests/overlaytests/device_non_system/res/values/overlayable.xml
similarity index 61%
copy from packages/SystemUI/res/layout/auth_biometric_icon.xml
copy to core/tests/overlaytests/device_non_system/res/values/overlayable.xml
index b2df63d..f8017bc 100644
--- a/packages/SystemUI/res/layout/auth_biometric_icon.xml
+++ b/core/tests/overlaytests/device_non_system/res/values/overlayable.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,12 +15,10 @@
~ limitations under the License.
-->
-
-<com.airbnb.lottie.LottieAnimationView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/biometric_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:contentDescription="@null"
- android:scaleType="fitXY"/>
\ No newline at end of file
+<resources>
+ <overlayable name="TestResources">
+ <policy type="public">
+ <item type="string" name="test_string" />
+ </policy>
+ </overlayable>
+</resources>
\ No newline at end of file
diff --git a/core/tests/overlaytests/device_non_system/res/values/strings.xml b/core/tests/overlaytests/device_non_system/res/values/strings.xml
new file mode 100644
index 0000000..ff501a0
--- /dev/null
+++ b/core/tests/overlaytests/device_non_system/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="test_string">Original</string>
+</resources>
\ No newline at end of file
diff --git a/core/tests/overlaytests/device_non_system/src/com/android/overlaytest/OverlayTest.java b/core/tests/overlaytests/device_non_system/src/com/android/overlaytest/OverlayTest.java
new file mode 100644
index 0000000..2b0fe6c
--- /dev/null
+++ b/core/tests/overlaytests/device_non_system/src/com/android/overlaytest/OverlayTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.overlaytest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.overlaytest.non_system.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * This test class is to verify overlay behavior for non-system apps.
+ */
+@RunWith(JUnit4.class)
+public class OverlayTest {
+ @Test
+ public void testStringOverlay() throws Throwable {
+ final LayoutInflater inflater = LayoutInflater.from(InstrumentationRegistry.getContext());
+ final View layout = inflater.inflate(R.layout.layout, null);
+ TextView tv = layout.findViewById(R.id.text_view_id);
+ assertNotNull(tv);
+ assertEquals("Overlaid", tv.getText().toString());
+ }
+}
diff --git a/core/tests/overlaytests/device_non_system/test-apps/AppOverlay/Android.bp b/core/tests/overlaytests/device_non_system/test-apps/AppOverlay/Android.bp
new file mode 100644
index 0000000..b5e6d9c
--- /dev/null
+++ b/core/tests/overlaytests/device_non_system/test-apps/AppOverlay/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "OverlayDeviceTestsNonSystem_AppOverlay",
+ team: "trendy_team_android_resources",
+ sdk_version: "current",
+ certificate: "platform",
+ aaptflags: ["--no-resource-removal"],
+}
diff --git a/core/tests/overlaytests/device_non_system/test-apps/AppOverlay/AndroidManifest.xml b/core/tests/overlaytests/device_non_system/test-apps/AppOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..4df80c0
--- /dev/null
+++ b/core/tests/overlaytests/device_non_system/test-apps/AppOverlay/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.non_system.app_overlay"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <application android:hasCode="false" />
+ <overlay android:targetPackage="com.android.overlaytest.non_system"
+ android:targetName="TestResources"
+ android:isStatic="true"
+ android:resourcesMap="@xml/overlays"/>
+</manifest>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_biometric_icon.xml b/core/tests/overlaytests/device_non_system/test-apps/AppOverlay/res/xml/overlays.xml
similarity index 61%
copy from packages/SystemUI/res/layout/auth_biometric_icon.xml
copy to core/tests/overlaytests/device_non_system/test-apps/AppOverlay/res/xml/overlays.xml
index b2df63d..d0d4bfe 100644
--- a/packages/SystemUI/res/layout/auth_biometric_icon.xml
+++ b/core/tests/overlaytests/device_non_system/test-apps/AppOverlay/res/xml/overlays.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,13 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-
-<com.airbnb.lottie.LottieAnimationView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/biometric_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:contentDescription="@null"
- android:scaleType="fitXY"/>
\ No newline at end of file
+<overlay>
+ <item target="string/test_string" value="Overlaid"/>
+</overlay>
\ No newline at end of file
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index bca741f..66b47da 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -52,6 +52,7 @@
<permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
+ <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
<permission name="android.permission.READ_WALLPAPER_INTERNAL"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.REQUEST_NETWORK_SCORES"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 6cf12de..483b693 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1045,12 +1045,6 @@
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
- "-1459414342866553129": {
- "message": "Current focused window being animated by recents. Overriding back callback to recents controller callback.",
- "level": "DEBUG",
- "group": "WM_DEBUG_BACK_PREVIEW",
- "at": "com\/android\/server\/wm\/BackNavigationController.java"
- },
"2881085074175114605": {
"message": "Focused window didn't have a valid surface drawn.",
"level": "DEBUG",
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 2f2215f..d1d7c14 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -16,8 +16,6 @@
package android.security;
-import android.compat.annotation.UnsupportedAppUsage;
-
/**
* This class provides some constants and helper methods related to Android's Keystore service.
* This class was originally much larger, but its functionality was superseded by other classes.
@@ -30,11 +28,4 @@
// Used for UID field to indicate the calling UID.
public static final int UID_SELF = -1;
-
- private static final KeyStore KEY_STORE = new KeyStore();
-
- @UnsupportedAppUsage
- public static KeyStore getInstance() {
- return KEY_STORE;
- }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index b8ac191..0a5a81b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -25,8 +25,8 @@
import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_DECOR_SURFACE_BOOSTED;
-import static androidx.window.extensions.embedding.DividerAttributes.RATIO_UNSET;
-import static androidx.window.extensions.embedding.DividerAttributes.WIDTH_UNSET;
+import static androidx.window.extensions.embedding.DividerAttributes.RATIO_SYSTEM_DEFAULT;
+import static androidx.window.extensions.embedding.DividerAttributes.WIDTH_SYSTEM_DEFAULT;
import static androidx.window.extensions.embedding.SplitAttributesHelper.isReversedLayout;
import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_BOTTOM;
import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_LEFT;
@@ -64,6 +64,9 @@
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.window.extensions.core.util.function.Consumer;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -76,12 +79,13 @@
* Manages the rendering and interaction of the divider.
*/
class DividerPresenter implements View.OnTouchListener {
+ static final float RATIO_EXPANDED_PRIMARY = 1.0f;
+ static final float RATIO_EXPANDED_SECONDARY = 0.0f;
private static final String WINDOW_NAME = "AE Divider";
private static final int VEIL_LAYER = 0;
private static final int DIVIDER_LAYER = 1;
// TODO(b/327067596) Update based on UX guidance.
- private static final Color DEFAULT_DIVIDER_COLOR = Color.valueOf(Color.BLACK);
private static final Color DEFAULT_PRIMARY_VEIL_COLOR = Color.valueOf(Color.BLACK);
private static final Color DEFAULT_SECONDARY_VEIL_COLOR = Color.valueOf(Color.GRAY);
@VisibleForTesting
@@ -162,54 +166,55 @@
return;
}
+ final SplitAttributes splitAttributes = topSplitContainer.getCurrentSplitAttributes();
+ final DividerAttributes dividerAttributes = splitAttributes.getDividerAttributes();
+
// Clean up the decor surface if DividerAttributes is null.
- final DividerAttributes dividerAttributes =
- topSplitContainer.getCurrentSplitAttributes().getDividerAttributes();
if (dividerAttributes == null) {
removeDecorSurfaceAndDivider(wct);
return;
}
- if (topSplitContainer.getCurrentSplitAttributes().getSplitType()
- instanceof SplitAttributes.SplitType.ExpandContainersSplitType) {
- // No divider is needed for ExpandContainersSplitType.
- removeDivider();
- return;
- }
+ // At this point, a divider is required.
- // Skip updating when the TFs have not been updated to match the SplitAttributes.
- if (topSplitContainer.getPrimaryContainer().getLastRequestedBounds().isEmpty()
- || topSplitContainer.getSecondaryContainer().getLastRequestedBounds()
- .isEmpty()) {
- return;
- }
-
+ // Create the decor surface if one is not available yet.
final SurfaceControl decorSurface = parentInfo.getDecorSurface();
if (decorSurface == null) {
// Clean up when the decor surface is currently unavailable.
removeDivider();
// Request to create the decor surface
- createOrMoveDecorSurface(wct, topSplitContainer.getPrimaryContainer());
+ createOrMoveDecorSurfaceLocked(wct, topSplitContainer.getPrimaryContainer());
return;
}
- // make the top primary container the owner of the decor surface.
- if (!Objects.equals(mDecorSurfaceOwner,
- topSplitContainer.getPrimaryContainer().getTaskFragmentToken())) {
- createOrMoveDecorSurface(wct, topSplitContainer.getPrimaryContainer());
+ // Update the decor surface owner if needed.
+ boolean isDraggableExpandType =
+ SplitAttributesHelper.isDraggableExpandType(splitAttributes);
+ final TaskFragmentContainer decorSurfaceOwnerContainer = isDraggableExpandType
+ ? topSplitContainer.getSecondaryContainer()
+ : topSplitContainer.getPrimaryContainer();
+
+ if (!Objects.equals(
+ mDecorSurfaceOwner, decorSurfaceOwnerContainer.getTaskFragmentToken())) {
+ createOrMoveDecorSurfaceLocked(wct, decorSurfaceOwnerContainer);
}
+ final boolean isVerticalSplit = isVerticalSplit(topSplitContainer);
+ final boolean isReversedLayout = isReversedLayout(
+ topSplitContainer.getCurrentSplitAttributes(),
+ parentInfo.getConfiguration());
updateProperties(
new Properties(
parentInfo.getConfiguration(),
dividerAttributes,
decorSurface,
- getInitialDividerPosition(topSplitContainer),
- isVerticalSplit(topSplitContainer),
- isReversedLayout(
- topSplitContainer.getCurrentSplitAttributes(),
- parentInfo.getConfiguration()),
- parentInfo.getDisplayId()));
+ getInitialDividerPosition(
+ topSplitContainer, isVerticalSplit, isReversedLayout),
+ isVerticalSplit,
+ isReversedLayout,
+ parentInfo.getDisplayId(),
+ isDraggableExpandType
+ ));
}
}
@@ -242,14 +247,21 @@
*
* See {@link TaskFragmentOperation#OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE}.
*/
- @GuardedBy("mLock")
- private void createOrMoveDecorSurface(
+ void createOrMoveDecorSurface(
@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer container) {
+ synchronized (mLock) {
+ createOrMoveDecorSurfaceLocked(wct, container);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void createOrMoveDecorSurfaceLocked(
+ @NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer container) {
+ mDecorSurfaceOwner = container.getTaskFragmentToken();
final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE)
.build();
- wct.addTaskFragmentOperation(container.getTaskFragmentToken(), operation);
- mDecorSurfaceOwner = container.getTaskFragmentToken();
+ wct.addTaskFragmentOperation(mDecorSurfaceOwner, operation);
}
@GuardedBy("mLock")
@@ -274,15 +286,28 @@
}
@VisibleForTesting
- static int getInitialDividerPosition(@NonNull SplitContainer splitContainer) {
+ static int getInitialDividerPosition(
+ @NonNull SplitContainer splitContainer,
+ boolean isVerticalSplit,
+ boolean isReversedLayout) {
final Rect primaryBounds =
splitContainer.getPrimaryContainer().getLastRequestedBounds();
final Rect secondaryBounds =
splitContainer.getSecondaryContainer().getLastRequestedBounds();
- if (isVerticalSplit(splitContainer)) {
- return Math.min(primaryBounds.right, secondaryBounds.right);
+ final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes();
+
+ if (SplitAttributesHelper.isDraggableExpandType(splitAttributes)) {
+ // If the container is fully expanded by dragging the divider, we display the divider
+ // on the edge.
+ final int dividerWidth = getDividerWidthPx(splitAttributes.getDividerAttributes());
+ final int fullyExpandedPosition = isVerticalSplit
+ ? primaryBounds.right - dividerWidth
+ : primaryBounds.bottom - dividerWidth;
+ return isReversedLayout ? fullyExpandedPosition : 0;
} else {
- return Math.min(primaryBounds.bottom, secondaryBounds.bottom);
+ return isVerticalSplit
+ ? Math.min(primaryBounds.right, secondaryBounds.right)
+ : Math.min(primaryBounds.bottom, secondaryBounds.bottom);
}
}
@@ -359,14 +384,14 @@
@VisibleForTesting
static int getBoundsOffsetForDivider(
int dividerWidthPx,
- @NonNull SplitAttributes.SplitType splitType,
+ @NonNull SplitType splitType,
@SplitPresenter.ContainerPosition int position) {
- if (splitType instanceof SplitAttributes.SplitType.ExpandContainersSplitType) {
- // No divider is needed for the ExpandContainersSplitType.
+ if (splitType instanceof ExpandContainersSplitType) {
+ // No divider offset is needed for the ExpandContainersSplitType.
return 0;
}
int primaryOffset;
- if (splitType instanceof final SplitAttributes.SplitType.RatioSplitType splitRatio) {
+ if (splitType instanceof final RatioSplitType splitRatio) {
// When a divider is present, both containers shrink by an amount proportional to their
// split ratio and sum to the width of the divider, so that the ending sizing of the
// containers still maintain the same ratio.
@@ -393,7 +418,8 @@
* Sanitizes and sets default values in the {@link DividerAttributes}.
*
* Unset values will be set with system default values. See
- * {@link DividerAttributes#WIDTH_UNSET} and {@link DividerAttributes#RATIO_UNSET}.
+ * {@link DividerAttributes#WIDTH_SYSTEM_DEFAULT} and
+ * {@link DividerAttributes#RATIO_SYSTEM_DEFAULT}.
*
* @param dividerAttributes input {@link DividerAttributes}
* @return a {@link DividerAttributes} that has all values properly set.
@@ -405,7 +431,7 @@
return null;
}
int widthDp = dividerAttributes.getWidthDp();
- if (widthDp == WIDTH_UNSET) {
+ if (widthDp == WIDTH_SYSTEM_DEFAULT) {
widthDp = DEFAULT_DIVIDER_WIDTH_DP;
}
@@ -416,12 +442,12 @@
}
float minRatio = dividerAttributes.getPrimaryMinRatio();
- if (minRatio == RATIO_UNSET) {
+ if (minRatio == RATIO_SYSTEM_DEFAULT) {
minRatio = DEFAULT_MIN_RATIO;
}
float maxRatio = dividerAttributes.getPrimaryMaxRatio();
- if (maxRatio == RATIO_UNSET) {
+ if (maxRatio == RATIO_SYSTEM_DEFAULT) {
maxRatio = DEFAULT_MAX_RATIO;
}
@@ -438,7 +464,7 @@
final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds();
mDividerPosition = calculateDividerPosition(
event, taskBounds, mRenderer.mDividerWidthPx, mProperties.mDividerAttributes,
- mProperties.mIsVerticalSplit, mProperties.mIsReversedLayout);
+ mProperties.mIsVerticalSplit, calculateMinPosition(), calculateMaxPosition());
mRenderer.setDividerPosition(mDividerPosition);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
@@ -456,23 +482,27 @@
}
}
- // Returns false so that the default button click callback is still triggered, i.e. the
- // button UI transitions into the "pressed" state.
- return false;
+ // Returns true to prevent the default button click callback. The button pressed state is
+ // set/unset when starting/finishing dragging.
+ return true;
}
@GuardedBy("mLock")
private void onStartDragging() {
mRenderer.mIsDragging = true;
+ mRenderer.mDragHandle.setPressed(mRenderer.mIsDragging);
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
mRenderer.updateSurface(t);
mRenderer.showVeils(t);
- final IBinder decorSurfaceOwner = mDecorSurfaceOwner;
// Callbacks must be executed on the executor to release mLock and prevent deadlocks.
mCallbackExecutor.execute(() -> {
mDragEventCallback.onStartDragging(
- wct -> setDecorSurfaceBoosted(wct, decorSurfaceOwner, true /* boosted */, t));
+ wct -> {
+ synchronized (mLock) {
+ setDecorSurfaceBoosted(wct, mDecorSurfaceOwner, true /* boosted */, t);
+ }
+ });
});
}
@@ -485,18 +515,62 @@
@GuardedBy("mLock")
private void onFinishDragging() {
+ mDividerPosition = adjustDividerPositionForSnapPoints(mDividerPosition);
+ mRenderer.setDividerPosition(mDividerPosition);
+
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
mRenderer.updateSurface(t);
mRenderer.hideVeils(t);
- final IBinder decorSurfaceOwner = mDecorSurfaceOwner;
// Callbacks must be executed on the executor to release mLock and prevent deadlocks.
+ // mDecorSurfaceOwner may change between here and when the callback is executed,
+ // e.g. when the decor surface owner becomes the secondary container when it is expanded to
+ // fullscreen.
mCallbackExecutor.execute(() -> {
mDragEventCallback.onFinishDragging(
mTaskId,
- wct -> setDecorSurfaceBoosted(wct, decorSurfaceOwner, false /* boosted */, t));
+ wct -> {
+ synchronized (mLock) {
+ setDecorSurfaceBoosted(wct, mDecorSurfaceOwner, false /* boosted */, t);
+ }
+ });
});
mRenderer.mIsDragging = false;
+ mRenderer.mDragHandle.setPressed(mRenderer.mIsDragging);
+ }
+
+ /**
+ * Returns the divider position adjusted for the min max ratio and fullscreen expansion.
+ *
+ * If the dragging position is above the {@link DividerAttributes#getPrimaryMaxRatio()} or below
+ * {@link DividerAttributes#getPrimaryMinRatio()} and
+ * {@link DividerAttributes#isDraggingToFullscreenAllowed} is {@code true}, the system will
+ * choose a snap algorithm to adjust the ending position to either fully expand one container or
+ * move the divider back to the specified min/max ratio.
+ *
+ * TODO(b/327067596) implement snap algorithm
+ *
+ * The adjusted divider position is in the range of [minPosition, maxPosition] for a split, 0
+ * for expanded right (bottom) container, or task width (height) minus the divider width for
+ * expanded left (top) container.
+ */
+ @GuardedBy("mLock")
+ private int adjustDividerPositionForSnapPoints(int dividerPosition) {
+ final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds();
+ final int minPosition = calculateMinPosition();
+ final int maxPosition = calculateMaxPosition();
+ final int fullyExpandedPosition = mProperties.mIsVerticalSplit
+ ? taskBounds.right - mRenderer.mDividerWidthPx
+ : taskBounds.bottom - mRenderer.mDividerWidthPx;
+ if (isDraggingToFullscreenAllowed(mProperties.mDividerAttributes)) {
+ if (dividerPosition < minPosition) {
+ return 0;
+ }
+ if (dividerPosition > maxPosition) {
+ return fullyExpandedPosition;
+ }
+ }
+ return Math.clamp(dividerPosition, minPosition, maxPosition);
}
private static void setDecorSurfaceBoosted(
@@ -520,7 +594,7 @@
@VisibleForTesting
static int calculateDividerPosition(@NonNull MotionEvent event, @NonNull Rect taskBounds,
int dividerWidthPx, @NonNull DividerAttributes dividerAttributes,
- boolean isVerticalSplit, boolean isReversedLayout) {
+ boolean isVerticalSplit, int minPosition, int maxPosition) {
// The touch event is in display space. Converting it into the task window space.
final int touchPositionInTaskSpace = isVerticalSplit
? (int) (event.getRawX()) - taskBounds.left
@@ -530,15 +604,31 @@
// position is offset by half of the divider width.
int dividerPosition = touchPositionInTaskSpace - dividerWidthPx / 2;
- // Limit the divider position to the min and max ratios set in DividerAttributes.
- // TODO(b/327536303) Handle when the divider is dragged to the edge.
- dividerPosition = Math.max(dividerPosition, calculateMinPosition(
- taskBounds, dividerWidthPx, dividerAttributes, isVerticalSplit, isReversedLayout));
- dividerPosition = Math.min(dividerPosition, calculateMaxPosition(
- taskBounds, dividerWidthPx, dividerAttributes, isVerticalSplit, isReversedLayout));
+ // If dragging to fullscreen is not allowed, limit the divider position to the min and max
+ // ratios set in DividerAttributes. Otherwise, dragging beyond the min and max ratios is
+ // temporarily allowed and the final ratio will be adjusted in onFinishDragging.
+ if (!isDraggingToFullscreenAllowed(dividerAttributes)) {
+ dividerPosition = Math.clamp(dividerPosition, minPosition, maxPosition);
+ }
return dividerPosition;
}
+ @GuardedBy("mLock")
+ private int calculateMinPosition() {
+ return calculateMinPosition(
+ mProperties.mConfiguration.windowConfiguration.getBounds(),
+ mRenderer.mDividerWidthPx, mProperties.mDividerAttributes,
+ mProperties.mIsVerticalSplit, mProperties.mIsReversedLayout);
+ }
+
+ @GuardedBy("mLock")
+ private int calculateMaxPosition() {
+ return calculateMaxPosition(
+ mProperties.mConfiguration.windowConfiguration.getBounds(),
+ mRenderer.mDividerWidthPx, mProperties.mDividerAttributes,
+ mProperties.mIsVerticalSplit, mProperties.mIsReversedLayout);
+ }
+
/** Calculates the min position of the divider that the user is allowed to drag to. */
@VisibleForTesting
static int calculateMinPosition(@NonNull Rect taskBounds, int dividerWidthPx,
@@ -581,13 +671,24 @@
mProperties.mConfiguration.windowConfiguration.getBounds(),
mRenderer.mDividerWidthPx,
mProperties.mIsVerticalSplit,
- mProperties.mIsReversedLayout);
+ mProperties.mIsReversedLayout,
+ calculateMinPosition(),
+ calculateMaxPosition(),
+ isDraggingToFullscreenAllowed(mProperties.mDividerAttributes));
}
}
+ private static boolean isDraggingToFullscreenAllowed(
+ @NonNull DividerAttributes dividerAttributes) {
+ // TODO(b/293654166) Use DividerAttributes.isDraggingToFullscreenAllowed when extension is
+ // updated.
+ return true;
+ }
+
/**
* Returns the new split ratio of the {@link SplitContainer} based on the current divider
* position.
+ *
* @param topSplitContainer the {@link SplitContainer} for which to compute the split ratio.
* @param dividerPosition the divider position. See {@link #mDividerPosition}.
* @param taskBounds the task bounds
@@ -599,7 +700,9 @@
* bottom-to-top. If {@code false}, the split is not reversed, i.e.
* left-to-right or top-to-bottom. See
* {@link SplitAttributesHelper#isReversedLayout}
- * @return the computed split ratio of the primary container.
+ * @return the computed split ratio of the primary container. If the primary container is fully
+ * expanded, {@link #RATIO_EXPANDED_PRIMARY} is returned. If the secondary container is fully
+ * expanded, {@link #RATIO_EXPANDED_SECONDARY} is returned.
*/
@VisibleForTesting
static float calculateNewSplitRatio(
@@ -608,15 +711,33 @@
@NonNull Rect taskBounds,
int dividerWidthPx,
boolean isVerticalSplit,
- boolean isReversedLayout) {
+ boolean isReversedLayout,
+ int minPosition,
+ int maxPosition,
+ boolean isDraggingToFullscreenAllowed) {
+
+ // Handle the fully expanded cases.
+ if (isDraggingToFullscreenAllowed) {
+ // The divider position is already adjusted by the snap algorithm in onFinishDragging.
+ // If the divider position is not in the range [minPosition, maxPosition], then one of
+ // the containers is fully expanded.
+ if (dividerPosition < minPosition) {
+ return isReversedLayout ? RATIO_EXPANDED_PRIMARY : RATIO_EXPANDED_SECONDARY;
+ }
+ if (dividerPosition > maxPosition) {
+ return isReversedLayout ? RATIO_EXPANDED_SECONDARY : RATIO_EXPANDED_PRIMARY;
+ }
+ } else {
+ dividerPosition = Math.clamp(dividerPosition, minPosition, maxPosition);
+ }
+
+ final TaskFragmentContainer primaryContainer = topSplitContainer.getPrimaryContainer();
+ final Rect origPrimaryBounds = primaryContainer.getLastRequestedBounds();
final int usableSize = isVerticalSplit
? taskBounds.width() - dividerWidthPx
: taskBounds.height() - dividerWidthPx;
- final TaskFragmentContainer primaryContainer = topSplitContainer.getPrimaryContainer();
- final Rect origPrimaryBounds = primaryContainer.getLastRequestedBounds();
-
- float newRatio;
+ final float newRatio;
if (isVerticalSplit) {
final int newPrimaryWidth = isReversedLayout
? (origPrimaryBounds.right - (dividerPosition + dividerWidthPx))
@@ -677,6 +798,7 @@
private final int mDisplayId;
private final boolean mIsReversedLayout;
+ private final boolean mIsDraggableExpandType;
@VisibleForTesting
Properties(
@@ -686,7 +808,8 @@
int initialDividerPosition,
boolean isVerticalSplit,
boolean isReversedLayout,
- int displayId) {
+ int displayId,
+ boolean isDraggableExpandType) {
mConfiguration = configuration;
mDividerAttributes = dividerAttributes;
mDecorSurface = decorSurface;
@@ -694,6 +817,7 @@
mIsVerticalSplit = isVerticalSplit;
mIsReversedLayout = isReversedLayout;
mDisplayId = displayId;
+ mIsDraggableExpandType = isDraggableExpandType;
}
/**
@@ -714,7 +838,8 @@
&& a.mInitialDividerPosition == b.mInitialDividerPosition
&& a.mIsVerticalSplit == b.mIsVerticalSplit
&& a.mDisplayId == b.mDisplayId
- && a.mIsReversedLayout == b.mIsReversedLayout;
+ && a.mIsReversedLayout == b.mIsReversedLayout
+ && a.mIsDraggableExpandType == b.mIsDraggableExpandType;
}
private static boolean areSameSurfaces(
@@ -761,6 +886,7 @@
private SurfaceControl mSecondaryVeil;
private boolean mIsDragging;
private int mDividerPosition;
+ private View mDragHandle;
private Renderer(@NonNull Properties properties, @NonNull View.OnTouchListener listener) {
mProperties = properties;
@@ -857,6 +983,7 @@
PixelFormat.TRANSLUCENT);
lp.setTitle(WINDOW_NAME);
mViewHost.setView(mDividerLayout, lp);
+ mViewHost.relayout(lp);
}
/**
@@ -867,7 +994,12 @@
*/
private void updateDivider(@NonNull SurfaceControl.Transaction t) {
mDividerLayout.removeAllViews();
- mDividerLayout.setBackgroundColor(DEFAULT_DIVIDER_COLOR.toArgb());
+ if (mProperties.mIsDraggableExpandType) {
+ // If a container is fully expanded, the divider overlays on the expanded container.
+ mDividerLayout.setBackgroundColor(Color.TRANSPARENT);
+ } else {
+ mDividerLayout.setBackgroundColor(mProperties.mDividerAttributes.getDividerColor());
+ }
if (mProperties.mDividerAttributes.getDividerType()
== DividerAttributes.DIVIDER_TYPE_DRAGGABLE) {
createVeils();
@@ -916,6 +1048,7 @@
}
button.setOnTouchListener(mListener);
+ mDragHandle = button;
mDividerLayout.addView(button);
}
@@ -928,7 +1061,7 @@
.setHidden(!visible)
.setCallsite("DividerManager.createChildSurface")
.setBufferSize(bounds.width(), bounds.height())
- .setColorLayer()
+ .setEffectLayer()
.build();
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitAttributesHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitAttributesHelper.java
index 042a68a6..4541a84 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitAttributesHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitAttributesHelper.java
@@ -20,6 +20,7 @@
import android.view.View;
import androidx.annotation.NonNull;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType;
/** Helper functions for {@link SplitAttributes} */
class SplitAttributesHelper {
@@ -43,4 +44,17 @@
"Invalid layout direction:" + splitAttributes.getLayoutDirection());
}
}
+
+ /**
+ * Returns whether the {@link SplitAttributes} is an {@link ExpandContainersSplitType} and it
+ * should show a draggable handle that allows the user to drag and restore it into a split.
+ * This state is a result of user dragging the divider to fully expand the secondary container.
+ */
+ static boolean isDraggableExpandType(@NonNull SplitAttributes splitAttributes) {
+ final DividerAttributes dividerAttributes = splitAttributes.getDividerAttributes();
+ return splitAttributes.getSplitType() instanceof ExpandContainersSplitType
+ && dividerAttributes != null
+ && dividerAttributes.getDividerType() == DividerAttributes.DIVIDER_TYPE_DRAGGABLE;
+
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index b9b86f0..46a3e7f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -21,6 +21,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE;
@@ -115,6 +116,11 @@
static final boolean ENABLE_SHELL_TRANSITIONS =
SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
+ // TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without
+ // association. It's not set in WM Extensions nor Wm Jetpack library currently.
+ private static final String KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY =
+ "androidx.window.extensions.embedding.shouldAssociateWithLaunchingActivity";
+
@VisibleForTesting
@GuardedBy("mLock")
final SplitPresenter mPresenter;
@@ -855,9 +861,9 @@
// container update.
updateDivider(wct, taskContainer);
- // If the last direct activity of the host task is dismissed and the overlay container is
- // the only taskFragment, the overlay container should also be dismissed.
- dismissOverlayContainerIfNeeded(wct, taskContainer);
+ // If the last direct activity of the host task is dismissed and there's an always-on-top
+ // overlay container in the task, the overlay container should also be dismissed.
+ dismissAlwaysOnTopOverlayIfNeeded(wct, taskContainer);
if (!shouldUpdateContainer) {
return;
@@ -1405,9 +1411,27 @@
launchPlaceholderIfNecessary(wct, activity, false /* isOnCreated */);
}
+ @GuardedBy("mLock")
+ private void onActivityPaused(@NonNull WindowContainerTransaction wct,
+ @NonNull Activity activity) {
+ // Checks if there's any finishing activity in paused state associate with an overlay
+ // container. #OnActivityPostDestroyed is a very late signal, which is called after activity
+ // is not visible and the next activity shows on screen.
+ if (!activity.isFinishing()) {
+ // onPaused is triggered without finishing. Early return.
+ return;
+ }
+ // Check if we should dismiss the overlay container with this finishing activity.
+ final IBinder activityToken = activity.getActivityToken();
+ for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
+ mTaskContainers.valueAt(i).onFinishingActivityPaused(wct, activityToken);
+ }
+ updateCallbackIfNecessary();
+ }
+
@VisibleForTesting
@GuardedBy("mLock")
- void onActivityDestroyed(@NonNull Activity activity) {
+ void onActivityDestroyed(@NonNull WindowContainerTransaction wct, @NonNull Activity activity) {
if (!activity.isFinishing()) {
// onDestroyed is triggered without finishing. This happens when the activity is
// relaunched. In this case, we don't want to cleanup the record.
@@ -1417,7 +1441,7 @@
// organizer.
final IBinder activityToken = activity.getActivityToken();
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- mTaskContainers.valueAt(i).onActivityDestroyed(activityToken);
+ mTaskContainers.valueAt(i).onActivityDestroyed(wct, activityToken);
}
// We didn't trigger the callback if there were any pending appeared activities, so check
// again after the pending is removed.
@@ -1597,7 +1621,8 @@
@Nullable Activity launchingActivity) {
return createEmptyContainer(wct, intent, taskId,
new ActivityStackAttributes.Builder().build(), launchingActivity,
- null /* overlayTag */, null /* launchOptions */);
+ null /* overlayTag */, null /* launchOptions */,
+ false /* shouldAssociateWithLaunchingActivity */);
}
/**
@@ -1612,7 +1637,7 @@
@NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
@NonNull ActivityStackAttributes activityStackAttributes,
@Nullable Activity launchingActivity, @Nullable String overlayTag,
- @Nullable Bundle launchOptions) {
+ @Nullable Bundle launchOptions, boolean associateLaunchingActivity) {
// We need an activity in the organizer process in the same Task to use as the owner
// activity, as well as to get the Task window info.
final Activity activityInTask;
@@ -1630,7 +1655,7 @@
}
final TaskFragmentContainer container = newContainer(null /* pendingAppearedActivity */,
intent, activityInTask, taskId, null /* pairedPrimaryContainer*/, overlayTag,
- launchOptions);
+ launchOptions, associateLaunchingActivity);
final IBinder taskFragmentToken = container.getTaskFragmentToken();
// Note that taskContainer will not exist before calling #newContainer if the container
// is the first embedded TF in the task.
@@ -1722,7 +1747,7 @@
@NonNull Activity activityInTask, int taskId) {
return newContainer(pendingAppearedActivity, null /* pendingAppearedIntent */,
activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */,
- null /* launchOptions */);
+ null /* launchOptions */, false /* associateLaunchingActivity */);
}
@GuardedBy("mLock")
@@ -1730,7 +1755,7 @@
@NonNull Activity activityInTask, int taskId) {
return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */,
- null /* launchOptions */);
+ null /* launchOptions */, false /* associateLaunchingActivity */);
}
@GuardedBy("mLock")
@@ -1739,7 +1764,7 @@
@NonNull TaskFragmentContainer pairedPrimaryContainer) {
return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
activityInTask, taskId, pairedPrimaryContainer, null /* tag */,
- null /* launchOptions */);
+ null /* launchOptions */, false /* associateLaunchingActivity */);
}
/**
@@ -1751,19 +1776,21 @@
* @param activityInTask activity in the same Task so that we can get the Task bounds
* if needed.
* @param taskId parent Task of the new TaskFragment.
- * @param pairedPrimaryContainer the paired primary {@link TaskFragmentContainer}. When it is
+ * @param pairedContainer the paired primary {@link TaskFragmentContainer}. When it is
* set, the new container will be added right above it.
* @param overlayTag The tag for the new created overlay container. It must be
* needed if {@code isOverlay} is {@code true}. Otherwise,
* it should be {@code null}.
* @param launchOptions The launch options bundle to create a container. Must be
* specified for overlay container.
+ * @param associateLaunchingActivity {@code true} to indicate this overlay container
+ * should associate with launching activity.
*/
@GuardedBy("mLock")
TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId,
- @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag,
- @Nullable Bundle launchOptions) {
+ @Nullable TaskFragmentContainer pairedContainer, @Nullable String overlayTag,
+ @Nullable Bundle launchOptions, boolean associateLaunchingActivity) {
if (activityInTask == null) {
throw new IllegalArgumentException("activityInTask must not be null,");
}
@@ -1773,8 +1800,8 @@
}
final TaskContainer taskContainer = mTaskContainers.get(taskId);
final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
- pendingAppearedIntent, taskContainer, this, pairedPrimaryContainer, overlayTag,
- launchOptions);
+ pendingAppearedIntent, taskContainer, this, pairedContainer, overlayTag,
+ launchOptions, associateLaunchingActivity ? activityInTask : null);
return container;
}
@@ -1963,7 +1990,7 @@
@NonNull TaskFragmentContainer container) {
final TaskContainer taskContainer = container.getTaskContainer();
- if (dismissOverlayContainerIfNeeded(wct, taskContainer)) {
+ if (dismissAlwaysOnTopOverlayIfNeeded(wct, taskContainer)) {
return;
}
@@ -1987,22 +2014,27 @@
}
}
- /** Dismisses the overlay container in the {@code taskContainer} if needed. */
+ /**
+ * Dismisses {@link TaskFragmentContainer#isAlwaysOnTopOverlay()} in the {@code taskContainer}
+ * if needed.
+ */
@GuardedBy("mLock")
- private boolean dismissOverlayContainerIfNeeded(@NonNull WindowContainerTransaction wct,
- @NonNull TaskContainer taskContainer) {
- final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer();
- if (overlayContainer == null) {
+ private boolean dismissAlwaysOnTopOverlayIfNeeded(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskContainer taskContainer) {
+ // Dismiss always-on-top overlay container if it's the only container in the task and
+ // there's no direct activity in the parent task.
+ final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
+ if (containers.size() != 1 || taskContainer.hasDirectActivity()) {
return false;
}
- // Dismiss the overlay container if it's the only container in the task and there's no
- // direct activity in the parent task.
- if (taskContainer.getTaskFragmentContainers().size() == 1
- && !taskContainer.hasDirectActivity()) {
- mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */);
- return true;
+
+ final TaskFragmentContainer container = containers.getLast();
+ if (!container.isAlwaysOnTopOverlay()) {
+ return false;
}
- return false;
+
+ mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependant */);
+ return true;
}
/**
@@ -2141,6 +2173,10 @@
return false;
}
+ if (container != null && container.getTaskContainer().isPlaceholderRuleSuppressed()) {
+ return false;
+ }
+
final TaskContainer.TaskProperties taskProperties = mPresenter.getTaskProperties(activity);
final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair(activity,
placeholderRule.getPlaceholderIntent());
@@ -2236,6 +2272,9 @@
if (SplitPresenter.shouldShowSplit(splitAttributes)) {
return false;
}
+ if (SplitPresenter.shouldShowPlaceholderWhenExpanded(splitAttributes)) {
+ return false;
+ }
mTransactionManager.getCurrentTransactionRecord()
.setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
@@ -2586,25 +2625,42 @@
/**
* Gets all overlay containers from all tasks in this process, or an empty list if there's
* no overlay container.
- * <p>
- * Note that we only support one overlay container for each task, but an app could have multiple
- * tasks.
*/
@VisibleForTesting
@GuardedBy("mLock")
@NonNull
- List<TaskFragmentContainer> getAllOverlayTaskFragmentContainers() {
+ List<TaskFragmentContainer> getAllNonFinishingOverlayContainers() {
final List<TaskFragmentContainer> overlayContainers = new ArrayList<>();
for (int i = 0; i < mTaskContainers.size(); i++) {
final TaskContainer taskContainer = mTaskContainers.valueAt(i);
- final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer();
- if (overlayContainer != null) {
- overlayContainers.add(overlayContainer);
- }
+ final List<TaskFragmentContainer> overlayContainersPerTask = taskContainer
+ .getTaskFragmentContainers()
+ .stream()
+ .filter(c -> c.isOverlay() && !c.isFinished())
+ .toList();
+ overlayContainers.addAll(overlayContainersPerTask);
}
return overlayContainers;
}
+ /**
+ * Creates an overlay container or updates a visible overlay container if its
+ * {@link TaskFragmentContainer#getTaskId()}, {@link TaskFragmentContainer#getOverlayTag()}
+ * and {@link TaskFragmentContainer#getAssociatedActivityToken()} matches.
+ * <p>
+ * This method will also dismiss any existing overlay container if:
+ * <ul>
+ * <li>it's visible but not meet the criteria to update overlay</li>
+ * <li>{@link TaskFragmentContainer#getOverlayTag()} matches but not meet the criteria to
+ * update overlay</li>
+ * </ul>
+ *
+ * @param wct the {@link WindowContainerTransaction}
+ * @param options the {@link ActivityOptions} to launch the overlay
+ * @param intent the intent of activity to launch
+ * @param launchActivity the activity to launch the overlay container
+ * @return the overlay container
+ */
@VisibleForTesting
// Suppress GuardedBy warning because lint ask to mark this method as
// @GuardedBy(container.mController.mLock), which is mLock itself
@@ -2615,8 +2671,10 @@
@NonNull WindowContainerTransaction wct, @NonNull Bundle options,
@NonNull Intent intent, @NonNull Activity launchActivity) {
final List<TaskFragmentContainer> overlayContainers =
- getAllOverlayTaskFragmentContainers();
+ getAllNonFinishingOverlayContainers();
final String overlayTag = Objects.requireNonNull(options.getString(KEY_OVERLAY_TAG));
+ final boolean associateLaunchingActivity = options
+ .getBoolean(KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY, true);
// If the requested bounds of OverlayCreateParams are smaller than minimum dimensions
// specified by Intent, expand the overlay container to fill the parent task instead.
@@ -2637,35 +2695,91 @@
final int taskId = getTaskId(launchActivity);
if (!overlayContainers.isEmpty()) {
for (final TaskFragmentContainer overlayContainer : overlayContainers) {
- if (!overlayTag.equals(overlayContainer.getOverlayTag())
- && taskId == overlayContainer.getTaskId()) {
- // If there's an overlay container with different tag shown in the same
- // task, dismiss the existing overlay container.
- mPresenter.cleanupContainer(wct, overlayContainer,
- false /* shouldFinishDependant */);
- }
- if (overlayTag.equals(overlayContainer.getOverlayTag())
- && taskId != overlayContainer.getTaskId()) {
+ final boolean isTopNonFinishingOverlay = overlayContainer.equals(
+ overlayContainer.getTaskContainer().getTopNonFinishingTaskFragmentContainer(
+ true /* includePin */, true /* includeOverlay */));
+ if (taskId != overlayContainer.getTaskId()) {
// If there's an overlay container with same tag in a different task,
// dismiss the overlay container since the tag must be unique per process.
+ if (overlayTag.equals(overlayContainer.getOverlayTag())) {
+ Log.w(TAG, "The overlay container with tag:"
+ + overlayContainer.getOverlayTag() + " is dismissed because"
+ + " there's an existing overlay container with the same tag but"
+ + " different task ID:" + overlayContainer.getTaskId() + ". "
+ + "The new associated activity is " + launchActivity);
+ mPresenter.cleanupContainer(wct, overlayContainer,
+ false /* shouldFinishDependant */);
+ }
+ continue;
+ }
+ if (!overlayTag.equals(overlayContainer.getOverlayTag())) {
+ // If there's an overlay container with different tag on top in the same
+ // task, dismiss the existing overlay container.
+ if (isTopNonFinishingOverlay) {
+ mPresenter.cleanupContainer(wct, overlayContainer,
+ false /* shouldFinishDependant */);
+ }
+ continue;
+ }
+ // The overlay container has the same tag and task ID with the new launching
+ // overlay container.
+ if (!isTopNonFinishingOverlay) {
+ // Dismiss the invisible overlay container regardless of activity
+ // association if it collides the tag of new launched overlay container .
+ Log.w(TAG, "The invisible overlay container with tag:"
+ + overlayContainer.getOverlayTag() + " is dismissed because"
+ + " there's a launching overlay container with the same tag."
+ + " The new associated activity is " + launchActivity);
mPresenter.cleanupContainer(wct, overlayContainer,
false /* shouldFinishDependant */);
+ continue;
}
- if (overlayTag.equals(overlayContainer.getOverlayTag())
- && taskId == overlayContainer.getTaskId()) {
- mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs,
- getMinDimensions(intent));
- // We can just return the updated overlay container and don't need to
- // check other condition since we only have one OverlayCreateParams, and
- // if the tag and task are matched, it's impossible to match another task
- // or tag since tags and tasks are all unique.
- return overlayContainer;
+ // Requesting an always-on-top overlay.
+ if (!associateLaunchingActivity) {
+ if (overlayContainer.isAssociatedWithActivity()) {
+ // Dismiss the overlay container since it has associated with an activity.
+ Log.w(TAG, "The overlay container with tag:"
+ + overlayContainer.getOverlayTag() + " is dismissed because"
+ + " there's an existing overlay container with the same tag but"
+ + " different associated launching activity. The overlay container"
+ + " doesn't associate with any activity.");
+ mPresenter.cleanupContainer(wct, overlayContainer,
+ false /* shouldFinishDependant */);
+ continue;
+ } else {
+ // The existing overlay container doesn't associate an activity as well.
+ // Just update the overlay and return.
+ // Note that going to this condition means the tag, task ID matches a
+ // visible always-on-top overlay, and won't dismiss any overlay any more.
+ mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs,
+ getMinDimensions(intent));
+ return overlayContainer;
+ }
}
+ if (launchActivity.getActivityToken()
+ != overlayContainer.getAssociatedActivityToken()) {
+ Log.w(TAG, "The overlay container with tag:"
+ + overlayContainer.getOverlayTag() + " is dismissed because"
+ + " there's an existing overlay container with the same tag but"
+ + " different associated launching activity. The new associated"
+ + " activity is " + launchActivity);
+ // The associated activity must be the same, or it will be dismissed.
+ mPresenter.cleanupContainer(wct, overlayContainer,
+ false /* shouldFinishDependant */);
+ continue;
+ }
+ // Reaching here means the launching activity launch an overlay container with the
+ // same task ID, tag, while there's a previously launching visible overlay
+ // container. We'll regard it as updating the existing overlay container.
+ mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs,
+ getMinDimensions(intent));
+ return overlayContainer;
+
}
}
// Launch the overlay container to the task with taskId.
return createEmptyContainer(wct, intent, taskId, attrs, launchActivity, overlayTag,
- options);
+ options, associateLaunchingActivity);
}
private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter {
@@ -2754,6 +2868,23 @@
}
@Override
+ public void onActivityPostPaused(@NonNull Activity activity) {
+ if (activity.isChild()) {
+ // Skip Activity that is child of another Activity (ActivityGroup) because it's
+ // window will just be a child of the parent Activity window.
+ return;
+ }
+ synchronized (mLock) {
+ final TransactionRecord transactionRecord = mTransactionManager
+ .startNewTransaction();
+ transactionRecord.setOriginType(TRANSIT_CLOSE);
+ SplitController.this.onActivityPaused(
+ transactionRecord.getTransaction(), activity);
+ transactionRecord.apply(false /* shouldApplyIndependently */);
+ }
+ }
+
+ @Override
public void onActivityPostDestroyed(@NonNull Activity activity) {
if (activity.isChild()) {
// Skip Activity that is child of another Activity (ActivityGroup) because it's
@@ -2761,7 +2892,12 @@
return;
}
synchronized (mLock) {
- SplitController.this.onActivityDestroyed(activity);
+ final TransactionRecord transactionRecord = mTransactionManager
+ .startNewTransaction();
+ transactionRecord.setOriginType(TRANSIT_CLOSE);
+ SplitController.this.onActivityDestroyed(
+ transactionRecord.getTransaction(), activity);
+ transactionRecord.apply(false /* shouldApplyIndependently */);
}
}
}
@@ -3111,7 +3247,12 @@
if (taskContainer != null) {
final DividerPresenter dividerPresenter =
mDividerPresenters.get(taskContainer.getTaskId());
- taskContainer.updateTopSplitContainerForDivider(dividerPresenter);
+ final List<TaskFragmentContainer> containersToFinish = new ArrayList<>();
+ taskContainer.updateTopSplitContainerForDivider(
+ dividerPresenter, containersToFinish);
+ for (final TaskFragmentContainer container : containersToFinish) {
+ mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
+ }
updateContainersInTask(wct, taskContainer);
}
action.accept(wct);
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 0d31266..6231ea0 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -467,6 +467,11 @@
reorderTaskFragmentToFront(wct,
pinnedContainer.getSecondaryContainer().getTaskFragmentToken());
}
+ final TaskFragmentContainer alwaysOnTopOverlayContainer = container.getTaskContainer()
+ .getAlwaysOnTopOverlayContainer();
+ if (alwaysOnTopOverlayContainer != null) {
+ reorderTaskFragmentToFront(wct, alwaysOnTopOverlayContainer.getTaskFragmentToken());
+ }
}
@Override
@@ -565,7 +570,7 @@
@Override
void setCompanionTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder primary,
- @Nullable IBinder secondary) {
+ @Nullable IBinder secondary) {
final TaskFragmentContainer container = mController.getContainer(primary);
if (container == null) {
throw new IllegalStateException("setCompanionTaskFragment on TaskFragment that is"
@@ -590,7 +595,12 @@
final Rect relativeBounds = sanitizeBounds(attributes.getRelativeBounds(), minDimensions,
taskBounds);
final boolean isFillParent = relativeBounds.isEmpty();
- final boolean isIsolatedNavigated = !isFillParent && container.isOverlay();
+ // Note that we only set isolated navigation for overlay container without activity
+ // association. Activity will be launched to an expanded container on top of the overlay
+ // if the overlay is associated with an activity. Thus, an overlay with activity association
+ // will never be isolated navigated.
+ final boolean isIsolatedNavigated = container.isOverlay()
+ && !container.isAssociatedWithActivity() && !isFillParent;
final boolean dimOnTask = !isFillParent
&& attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK
&& Flags.fullscreenDimFlag();
@@ -716,6 +726,12 @@
return !(splitAttributes.getSplitType() instanceof ExpandContainersSplitType);
}
+ static boolean shouldShowPlaceholderWhenExpanded(@NonNull SplitAttributes splitAttributes) {
+ // The placeholder should be kept if the expand split type is a result of user dragging
+ // the divider.
+ return SplitAttributesHelper.isDraggableExpandType(splitAttributes);
+ }
+
@NonNull
SplitAttributes computeSplitAttributes(@NonNull TaskProperties taskProperties,
@NonNull SplitRule rule, @NonNull SplitAttributes defaultSplitAttributes,
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index a215bdf..fdf0910 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -22,6 +22,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.inMultiWindowMode;
+import static androidx.window.extensions.embedding.DividerPresenter.RATIO_EXPANDED_PRIMARY;
+import static androidx.window.extensions.embedding.DividerPresenter.RATIO_EXPANDED_SECONDARY;
+
import android.app.Activity;
import android.app.ActivityClient;
import android.app.WindowConfiguration;
@@ -40,6 +43,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType;
import java.util.ArrayList;
import java.util.List;
@@ -64,9 +70,11 @@
@Nullable
private SplitPinContainer mSplitPinContainer;
- /** The overlay container in this Task. */
+ /**
+ * The {@link TaskFragmentContainer#isAlwaysOnTopOverlay()} in the Task.
+ */
@Nullable
- private TaskFragmentContainer mOverlayContainer;
+ private TaskFragmentContainer mAlwaysOnTopOverlayContainer;
@NonNull
private final Configuration mConfiguration;
@@ -89,6 +97,25 @@
final Set<IBinder> mFinishedContainer = new ArraySet<>();
/**
+ * The {@link RatioSplitType} that will be applied to newly added containers. This is to ensure
+ * the required UX that, after user dragging the divider, the split ratio is persistent after
+ * launching a new activity into a new TaskFragment in the same Task.
+ */
+ private RatioSplitType mOverrideSplitType;
+
+ /**
+ * If {@code true}, suppress the placeholder rules in the {@link TaskContainer}.
+ * <p>
+ * This is used in case the user drags the divider to fully expand the primary container and
+ * dismiss the secondary container while a {@link SplitPlaceholderRule} is used. Without this
+ * flag, after dismissing the secondary container, a placeholder will be launched again.
+ * <p>
+ * This flag is set true when the primary container is fully expanded and cleared when a new
+ * split is added to the {@link TaskContainer}.
+ */
+ private boolean mPlaceholderRuleSuppressed;
+
+ /**
* The {@link TaskContainer} constructor
*
* @param taskId The ID of the Task, which must match {@link Activity#getTaskId()} with
@@ -211,10 +238,19 @@
return mContainers.isEmpty() && mFinishedContainer.isEmpty();
}
- /** Called when the activity is destroyed. */
- void onActivityDestroyed(@NonNull IBinder activityToken) {
+ /** Called when the activity {@link Activity#isFinishing()} and paused. */
+ void onFinishingActivityPaused(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder activityToken) {
for (TaskFragmentContainer container : mContainers) {
- container.onActivityDestroyed(activityToken);
+ container.onFinishingActivityPaused(wct, activityToken);
+ }
+ }
+
+ /** Called when the activity is destroyed. */
+ void onActivityDestroyed(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder activityToken) {
+ for (TaskFragmentContainer container : mContainers) {
+ container.onActivityDestroyed(wct, activityToken);
}
}
@@ -282,10 +318,12 @@
return null;
}
- /** Returns the overlay container in the task, or {@code null} if it doesn't exist. */
+ /**
+ * Returns the always-on-top overlay container in the task, or {@code null} if it doesn't exist.
+ */
@Nullable
- TaskFragmentContainer getOverlayContainer() {
- return mOverlayContainer;
+ TaskFragmentContainer getAlwaysOnTopOverlayContainer() {
+ return mAlwaysOnTopOverlayContainer;
}
int indexOf(@NonNull TaskFragmentContainer child) {
@@ -313,6 +351,11 @@
}
void addSplitContainer(@NonNull SplitContainer splitContainer) {
+ // Reset the placeholder rule suppression when a new split container is added.
+ mPlaceholderRuleSuppressed = false;
+
+ applyOverrideSplitTypeIfNeeded(splitContainer);
+
if (splitContainer instanceof SplitPinContainer) {
mSplitPinContainer = (SplitPinContainer) splitContainer;
mSplitContainers.add(splitContainer);
@@ -327,6 +370,39 @@
}
}
+ boolean isPlaceholderRuleSuppressed() {
+ return mPlaceholderRuleSuppressed;
+ }
+
+ // If there is an override SplitType due to user dragging the divider, the split ratio should
+ // be applied to newly added SplitContainers.
+ private void applyOverrideSplitTypeIfNeeded(@NonNull SplitContainer splitContainer) {
+ if (mOverrideSplitType == null) {
+ return;
+ }
+ final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes();
+ final DividerAttributes dividerAttributes = splitAttributes.getDividerAttributes();
+ if (!(splitAttributes.getSplitType() instanceof RatioSplitType)) {
+ // Skip if the original split type is not a ratio type.
+ return;
+ }
+ if (dividerAttributes == null
+ || dividerAttributes.getDividerType() != DividerAttributes.DIVIDER_TYPE_DRAGGABLE) {
+ // Skip if the split does not have a draggable divider.
+ return;
+ }
+ updateDefaultSplitAttributes(splitContainer, mOverrideSplitType);
+ }
+
+ private static void updateDefaultSplitAttributes(
+ @NonNull SplitContainer splitContainer, @NonNull SplitType overrideSplitType) {
+ splitContainer.updateDefaultSplitAttributes(
+ new SplitAttributes.Builder(splitContainer.getDefaultSplitAttributes())
+ .setSplitType(overrideSplitType)
+ .build()
+ );
+ }
+
void removeSplitContainers(@NonNull List<SplitContainer> containers) {
mSplitContainers.removeAll(containers);
}
@@ -398,18 +474,47 @@
return mContainers;
}
- void updateTopSplitContainerForDivider(@NonNull DividerPresenter dividerPresenter) {
+ void updateTopSplitContainerForDivider(
+ @NonNull DividerPresenter dividerPresenter,
+ @NonNull List<TaskFragmentContainer> outContainersToFinish) {
final SplitContainer topSplitContainer = getTopNonFinishingSplitContainer();
if (topSplitContainer == null) {
return;
}
-
+ final TaskFragmentContainer primaryContainer = topSplitContainer.getPrimaryContainer();
final float newRatio = dividerPresenter.calculateNewSplitRatio(topSplitContainer);
- topSplitContainer.updateDefaultSplitAttributes(
- new SplitAttributes.Builder(topSplitContainer.getDefaultSplitAttributes())
- .setSplitType(new SplitAttributes.SplitType.RatioSplitType(newRatio))
- .build()
- );
+
+ // If the primary container is fully expanded, we should finish all the associated
+ // secondary containers.
+ if (newRatio == RATIO_EXPANDED_PRIMARY) {
+ for (final SplitContainer splitContainer : mSplitContainers) {
+ if (primaryContainer == splitContainer.getPrimaryContainer()) {
+ outContainersToFinish.add(splitContainer.getSecondaryContainer());
+ }
+ }
+
+ // Temporarily suppress the placeholder rule in the TaskContainer. This will be restored
+ // if a new split is added into the TaskContainer.
+ mPlaceholderRuleSuppressed = true;
+
+ mOverrideSplitType = null;
+ return;
+ }
+
+ final SplitType newSplitType;
+ if (newRatio == RATIO_EXPANDED_SECONDARY) {
+ newSplitType = new ExpandContainersSplitType();
+ // We do not want to apply ExpandContainersSplitType to new split containers.
+ mOverrideSplitType = null;
+ } else {
+ // We save the override RatioSplitType and apply to new split containers.
+ newSplitType = mOverrideSplitType = new RatioSplitType(newRatio);
+ }
+ for (final SplitContainer splitContainer : mSplitContainers) {
+ if (primaryContainer == splitContainer.getPrimaryContainer()) {
+ updateDefaultSplitAttributes(splitContainer, newSplitType);
+ }
+ }
}
@Nullable
@@ -430,7 +535,21 @@
updateSplitPinContainerIfNecessary();
// Update overlay container after split pin container since the overlay should be on top of
// pin container.
- updateOverlayContainerIfNecessary();
+ updateAlwaysOnTopOverlayIfNecessary();
+ }
+
+ private void updateAlwaysOnTopOverlayIfNecessary() {
+ final List<TaskFragmentContainer> alwaysOnTopOverlays = mContainers
+ .stream().filter(TaskFragmentContainer::isAlwaysOnTopOverlay).toList();
+ if (alwaysOnTopOverlays.size() > 1) {
+ throw new IllegalStateException("There must be at most one always-on-top overlay "
+ + "container per Task");
+ }
+ mAlwaysOnTopOverlayContainer = alwaysOnTopOverlays.isEmpty()
+ ? null : alwaysOnTopOverlays.getFirst();
+ if (mAlwaysOnTopOverlayContainer != null) {
+ moveContainerToLastIfNecessary(mAlwaysOnTopOverlayContainer);
+ }
}
private void updateSplitPinContainerIfNecessary() {
@@ -458,18 +577,6 @@
}
}
- private void updateOverlayContainerIfNecessary() {
- final List<TaskFragmentContainer> overlayContainers = mContainers.stream()
- .filter(TaskFragmentContainer::isOverlay).toList();
- if (overlayContainers.size() > 1) {
- throw new IllegalStateException("There must be at most one overlay container per Task");
- }
- mOverlayContainer = overlayContainers.isEmpty() ? null : overlayContainers.get(0);
- if (mOverlayContainer != null) {
- moveContainerToLastIfNecessary(mOverlayContainer);
- }
- }
-
/** Moves the {@code container} to the last to align taskFragments' z-order. */
private void moveContainerToLastIfNecessary(@NonNull TaskFragmentContainer container) {
final int index = mContainers.indexOf(container);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index e20a3e0..094ebcb 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -113,6 +113,18 @@
@NonNull
private final Bundle mLaunchOptions = new Bundle();
+ /**
+ * The associated {@link Activity#getActivityToken()} of the overlay container.
+ * Must be {@code null} for non-overlay container.
+ * <p>
+ * If an overlay container is associated with an activity, this overlay container will be
+ * dismissed when the associated activity is destroyed. If the overlay container is visible,
+ * activity will be launched on top of the overlay container and expanded to fill the parent
+ * container.
+ */
+ @Nullable
+ private final IBinder mAssociatedActivityToken;
+
/** Indicates whether the container was cleaned up after the last activity was removed. */
private boolean mIsFinished;
@@ -178,7 +190,7 @@
/**
* @see #TaskFragmentContainer(Activity, Intent, TaskContainer, SplitController,
- * TaskFragmentContainer, String, Bundle)
+ * TaskFragmentContainer, String, Bundle, Activity)
*/
TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent,
@@ -187,7 +199,7 @@
@Nullable TaskFragmentContainer pairedPrimaryContainer) {
this(pendingAppearedActivity, pendingAppearedIntent, taskContainer,
controller, pairedPrimaryContainer, null /* overlayTag */,
- null /* launchOptions */);
+ null /* launchOptions */, null /* associatedActivity */);
}
/**
@@ -197,12 +209,14 @@
* @param overlayTag Sets to indicate this taskFragment is an overlay container
* @param launchOptions The launch options to create this container. Must not be
* {@code null} for an overlay container
+ * @param associatedActivity the associated activity of the overlay container. Must be
+ * {@code null} for a non-overlay container.
*/
TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull TaskContainer taskContainer,
@NonNull SplitController controller,
@Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag,
- @Nullable Bundle launchOptions) {
+ @Nullable Bundle launchOptions, @Nullable Activity associatedActivity) {
if ((pendingAppearedActivity == null && pendingAppearedIntent == null)
|| (pendingAppearedActivity != null && pendingAppearedIntent != null)) {
throw new IllegalArgumentException(
@@ -214,7 +228,13 @@
mOverlayTag = overlayTag;
if (overlayTag != null) {
Objects.requireNonNull(launchOptions);
+ } else if (associatedActivity != null) {
+ throw new IllegalArgumentException("Associated activity must be null for "
+ + "non-overlay activity.");
}
+ mAssociatedActivityToken = associatedActivity != null
+ ? associatedActivity.getActivityToken() : null;
+
if (launchOptions != null) {
mLaunchOptions.putAll(launchOptions);
}
@@ -420,14 +440,38 @@
}
}
+ /** Called when the activity {@link Activity#isFinishing()} and paused. */
+ void onFinishingActivityPaused(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder activityToken) {
+ finishSelfWithActivityIfNeeded(wct, activityToken);
+ }
+
/** Called when the activity is destroyed. */
- void onActivityDestroyed(@NonNull IBinder activityToken) {
+ void onActivityDestroyed(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder activityToken) {
removePendingAppearedActivity(activityToken);
if (mInfo != null) {
// Remove the activity now because there can be a delay before the server callback.
mInfo.getActivities().remove(activityToken);
}
mActivitiesToFinishOnExit.remove(activityToken);
+ finishSelfWithActivityIfNeeded(wct, activityToken);
+ }
+
+ @VisibleForTesting
+ void finishSelfWithActivityIfNeeded(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder activityToken) {
+ if (mIsFinished) {
+ return;
+ }
+ // Early return if this container is not an overlay with activity association.
+ if (!isOverlay() || !isAssociatedWithActivity()) {
+ return;
+ }
+ if (mAssociatedActivityToken == activityToken) {
+ // If the associated activity is destroyed, also finish this overlay container.
+ mController.mPresenter.cleanupContainer(wct, this, false /* shouldFinishDependent */);
+ }
}
@Nullable
@@ -961,6 +1005,32 @@
return mLaunchOptions;
}
+ /**
+ * Returns the associated Activity token of this overlay container. It must be {@code null}
+ * for non-overlay container.
+ * <p>
+ * If an overlay container is associated with an activity, this overlay container will be
+ * dismissed when the associated activity is destroyed. If the overlay container is visible,
+ * activity will be launched on top of the overlay container and expanded to fill the parent
+ * container.
+ */
+ @Nullable
+ IBinder getAssociatedActivityToken() {
+ return mAssociatedActivityToken;
+ }
+
+ boolean isAssociatedWithActivity() {
+ return mAssociatedActivityToken != null;
+ }
+
+ /**
+ * Returns {@code true} if the overlay container should be always on top, which should be
+ * a non-fill-parent overlay without activity association.
+ */
+ boolean isAlwaysOnTopOverlay() {
+ return isOverlay() && !isAssociatedWithActivity();
+ }
+
@Override
public String toString() {
return toString(true /* includeContainersToFinishOnExit */);
@@ -980,6 +1050,7 @@
+ " runningActivityCount=" + getRunningActivityCount()
+ " isFinished=" + mIsFinished
+ " overlayTag=" + mOverlayTag
+ + " associatedActivity" + mAssociatedActivityToken
+ " lastRequestedBounds=" + mLastRequestedBounds
+ " pendingAppearedActivities=" + mPendingAppearedActivities
+ (includeContainersToFinishOnExit ? " containersToFinishOnExit="
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
index 47d01da..de0171d 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
@@ -144,10 +144,12 @@
new Configuration(),
DEFAULT_DIVIDER_ATTRIBUTES,
mSurfaceControl,
- getInitialDividerPosition(mSplitContainer),
+ getInitialDividerPosition(
+ mSplitContainer, true /* isVerticalSplit */, false /* isReversedLayout */),
true /* isVerticalSplit */,
false /* isReversedLayout */,
- Display.DEFAULT_DISPLAY);
+ Display.DEFAULT_DISPLAY,
+ false /* isDraggableExpandType */);
mDividerPresenter = new DividerPresenter(
MOCK_TASK_ID, mDragEventCallback, mock(Executor.class));
@@ -348,7 +350,8 @@
dividerWidthPx,
dividerAttributes,
true /* isVerticalSplit */,
- false /* isReversedLayout */));
+ 0 /* minPosition */,
+ 900 /* maxPosition */));
// Top-to-bottom split
when(event.getRawY()).thenReturn(1000f); // Touch event is in display space
@@ -361,7 +364,8 @@
dividerWidthPx,
dividerAttributes,
false /* isVerticalSplit */,
- false /* isReversedLayout */));
+ 0 /* minPosition */,
+ 900 /* maxPosition */));
}
@Test
@@ -453,7 +457,6 @@
final Rect primaryBounds = new Rect(0, 0, 500, 2000);
final Rect secondaryBounds = new Rect(600, 0, 1100, 2000);
final int dividerWidthPx = 100;
- final int dividerPosition = 300;
final TaskFragmentContainer mockPrimaryContainer =
createMockTaskFragmentContainer(mPrimaryContainerToken, primaryBounds);
@@ -462,6 +465,8 @@
when(mSplitContainer.getPrimaryContainer()).thenReturn(mockPrimaryContainer);
when(mSplitContainer.getSecondaryContainer()).thenReturn(mockSecondaryContainer);
+ // Test the normal case
+ int dividerPosition = 300;
assertEquals(
0.3f, // Primary is 300px after dragging.
DividerPresenter.calculateNewSplitRatio(
@@ -470,7 +475,43 @@
taskBounds,
dividerWidthPx,
true /* isVerticalSplit */,
- false /* isReversedLayout */),
+ false /* isReversedLayout */,
+ 200 /* minPosition */,
+ 1000 /* maxPosition */,
+ false /* isDraggingToFullscreenAllowed */),
+ 0.0001 /* delta */);
+
+ // Test the case when dragging to fullscreen is allowed and divider is dragged to the edge
+ dividerPosition = 0;
+ assertEquals(
+ DividerPresenter.RATIO_EXPANDED_SECONDARY,
+ DividerPresenter.calculateNewSplitRatio(
+ mSplitContainer,
+ dividerPosition,
+ taskBounds,
+ dividerWidthPx,
+ true /* isVerticalSplit */,
+ false /* isReversedLayout */,
+ 200 /* minPosition */,
+ 1000 /* maxPosition */,
+ true /* isDraggingToFullscreenAllowed */),
+ 0.0001 /* delta */);
+
+ // Test the case when dragging to fullscreen is not allowed and divider is dragged to the
+ // edge.
+ dividerPosition = 0;
+ assertEquals(
+ 0.2f, // Adjusted to the minPosition 200
+ DividerPresenter.calculateNewSplitRatio(
+ mSplitContainer,
+ dividerPosition,
+ taskBounds,
+ dividerWidthPx,
+ true /* isVerticalSplit */,
+ false /* isReversedLayout */,
+ 200 /* minPosition */,
+ 1000 /* maxPosition */,
+ false /* isDraggingToFullscreenAllowed */),
0.0001 /* delta */);
}
@@ -482,7 +523,6 @@
final Rect primaryBounds = new Rect(0, 0, 2000, 1100);
final Rect secondaryBounds = new Rect(0, 0, 2000, 500);
final int dividerWidthPx = 100;
- final int dividerPosition = 300;
final TaskFragmentContainer mockPrimaryContainer =
createMockTaskFragmentContainer(mPrimaryContainerToken, primaryBounds);
@@ -491,6 +531,8 @@
when(mSplitContainer.getPrimaryContainer()).thenReturn(mockPrimaryContainer);
when(mSplitContainer.getSecondaryContainer()).thenReturn(mockSecondaryContainer);
+ // Test the normal case
+ int dividerPosition = 300;
assertEquals(
// After dragging, secondary is [0, 0, 2000, 300]. Primary is [0, 400, 2000, 1100].
0.7f,
@@ -500,7 +542,46 @@
taskBounds,
dividerWidthPx,
false /* isVerticalSplit */,
- true /* isReversedLayout */),
+ true /* isReversedLayout */,
+ 200 /* minPosition */,
+ 1000 /* maxPosition */,
+ true /* isDraggingToFullscreenAllowed */),
+ 0.0001 /* delta */);
+
+ // Test the case when dragging to fullscreen is not allowed and divider is dragged to the
+ // edge.
+ dividerPosition = 0;
+ assertEquals(
+ // The primary (bottom) container is expanded
+ DividerPresenter.RATIO_EXPANDED_PRIMARY,
+ DividerPresenter.calculateNewSplitRatio(
+ mSplitContainer,
+ dividerPosition,
+ taskBounds,
+ dividerWidthPx,
+ false /* isVerticalSplit */,
+ true /* isReversedLayout */,
+ 200 /* minPosition */,
+ 1000 /* maxPosition */,
+ true /* isDraggingToFullscreenAllowed */),
+ 0.0001 /* delta */);
+
+ // Test the case when dragging to fullscreen is not allowed and divider is dragged to the
+ // edge.
+ dividerPosition = 0;
+ assertEquals(
+ // Adjusted to minPosition 200, so the primary (bottom) container is 800.
+ 0.8f,
+ DividerPresenter.calculateNewSplitRatio(
+ mSplitContainer,
+ dividerPosition,
+ taskBounds,
+ dividerWidthPx,
+ false /* isVerticalSplit */,
+ true /* isReversedLayout */,
+ 200 /* minPosition */,
+ 1000 /* maxPosition */,
+ false /* isDraggingToFullscreenAllowed */),
0.0001 /* delta */);
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 28fbadb..b1b1984 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -39,6 +39,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -177,37 +178,59 @@
}
@Test
- public void testGetOverlayContainers() {
- assertThat(mSplitController.getAllOverlayTaskFragmentContainers()).isEmpty();
+ public void testGetAllNonFinishingOverlayContainers() {
+ assertThat(mSplitController.getAllNonFinishingOverlayContainers()).isEmpty();
final TaskFragmentContainer overlayContainer1 =
createTestOverlayContainer(TASK_ID, "test1");
- assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+ assertThat(mSplitController.getAllNonFinishingOverlayContainers())
.containsExactly(overlayContainer1);
- assertThrows(
- "The exception must throw if there are two overlay containers in the same task.",
- IllegalStateException.class,
- () -> createTestOverlayContainer(TASK_ID, "test2"));
+ final TaskFragmentContainer overlayContainer2 =
+ createTestOverlayContainer(TASK_ID, "test2");
+
+ assertThat(mSplitController.getAllNonFinishingOverlayContainers())
+ .containsExactly(overlayContainer1, overlayContainer2);
final TaskFragmentContainer overlayContainer3 =
createTestOverlayContainer(TASK_ID + 1, "test3");
- assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
- .containsExactly(overlayContainer1, overlayContainer3);
+ assertThat(mSplitController.getAllNonFinishingOverlayContainers())
+ .containsExactly(overlayContainer1, overlayContainer2, overlayContainer3);
+
+ final TaskFragmentContainer finishingOverlayContainer =
+ createTestOverlayContainer(TASK_ID, "test4");
+ spyOn(finishingOverlayContainer);
+ doReturn(true).when(finishingOverlayContainer).isFinished();
+
+ assertThat(mSplitController.getAllNonFinishingOverlayContainers())
+ .containsExactly(overlayContainer1, overlayContainer2, overlayContainer3);
}
@Test
- public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_anotherTagInTask_dismissOverlay() {
+ public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_anotherTagInTask() {
+ createExistingOverlayContainers(false /* visible */);
+ createMockTaskFragmentContainer(mActivity);
+
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test3");
+
+ assertWithMessage("overlayContainer1 is still there since it's not visible.")
+ .that(mSplitController.getAllNonFinishingOverlayContainers())
+ .containsExactly(mOverlayContainer1, mOverlayContainer2, overlayContainer);
+ }
+
+ @Test
+ public void testCreateOrUpdateOverlay_visibleOverlaySameTagInTask_dismissOverlay() {
createExistingOverlayContainers();
final TaskFragmentContainer overlayContainer =
createOrUpdateOverlayTaskFragmentIfNeeded("test3");
- assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
- + " is launched to the same task")
- .that(mSplitController.getAllOverlayTaskFragmentContainers())
+ assertWithMessage("overlayContainer1 must be dismissed since it's visible"
+ + " in the same task.")
+ .that(mSplitController.getAllNonFinishingOverlayContainers())
.containsExactly(mOverlayContainer2, overlayContainer);
}
@@ -222,23 +245,24 @@
assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
+ " is launched with the same tag as an existing overlay container in a different "
+ "task")
- .that(mSplitController.getAllOverlayTaskFragmentContainers())
+ .that(mSplitController.getAllNonFinishingOverlayContainers())
.containsExactly(mOverlayContainer2, overlayContainer);
}
@Test
- public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_sameTagAndTask_updateOverlay() {
+ public void testCreateOrUpdateOverlay_sameTagTaskAndActivity_updateOverlay() {
createExistingOverlayContainers();
final Rect bounds = new Rect(0, 0, 100, 100);
mSplitController.setActivityStackAttributesCalculator(params ->
new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- "test1");
+ mOverlayContainer1.getOverlayTag(),
+ mOverlayContainer1.getTopNonFinishingActivity());
assertWithMessage("overlayContainer1 must be updated since the new overlay container"
+ " is launched with the same tag and task")
- .that(mSplitController.getAllOverlayTaskFragmentContainers())
+ .that(mSplitController.getAllNonFinishingOverlayContainers())
.containsExactly(mOverlayContainer1, mOverlayContainer2);
assertThat(overlayContainer).isEqualTo(mOverlayContainer1);
@@ -247,6 +271,37 @@
}
@Test
+ public void testCreateOrUpdateOverlay_sameTagAndTaskButNotActivity_dismissOverlay() {
+ createExistingOverlayContainers();
+
+ final Rect bounds = new Rect(0, 0, 100, 100);
+ mSplitController.setActivityStackAttributesCalculator(params ->
+ new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
+ final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
+ mOverlayContainer1.getOverlayTag(), mActivity);
+
+ assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
+ + " is associated with different launching activity")
+ .that(mSplitController.getAllNonFinishingOverlayContainers())
+ .containsExactly(mOverlayContainer2, overlayContainer);
+ }
+
+ @Test
+ public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_dismissOverlay() {
+ createExistingOverlayContainers(false /* visible */);
+ createMockTaskFragmentContainer(mActivity);
+
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test2");
+
+ // OverlayContainer2 is dismissed since new container is launched with the
+ // same tag in different task.
+ assertWithMessage("overlayContainer1 must be dismissed")
+ .that(mSplitController.getAllNonFinishingOverlayContainers())
+ .containsExactly(mOverlayContainer1, overlayContainer);
+ }
+
+ @Test
public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_dismissMultipleOverlays() {
createExistingOverlayContainers();
@@ -257,15 +312,19 @@
// different tag. OverlayContainer2 is dismissed since new container is launched with the
// same tag in different task.
assertWithMessage("overlayContainer1 and overlayContainer2 must be dismissed")
- .that(mSplitController.getAllOverlayTaskFragmentContainers())
+ .that(mSplitController.getAllNonFinishingOverlayContainers())
.containsExactly(overlayContainer);
}
private void createExistingOverlayContainers() {
- mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1");
- mOverlayContainer2 = createTestOverlayContainer(TASK_ID + 1, "test2");
+ createExistingOverlayContainers(true /* visible */);
+ }
+
+ private void createExistingOverlayContainers(boolean visible) {
+ mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1", visible);
+ mOverlayContainer2 = createTestOverlayContainer(TASK_ID + 1, "test2", visible);
List<TaskFragmentContainer> overlayContainers = mSplitController
- .getAllOverlayTaskFragmentContainers();
+ .getAllNonFinishingOverlayContainers();
assertThat(overlayContainers).containsExactly(mOverlayContainer1, mOverlayContainer2);
}
@@ -294,9 +353,9 @@
new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
final TaskFragmentContainer overlayContainer =
createOrUpdateOverlayTaskFragmentIfNeeded("test");
- setupTaskFragmentInfo(overlayContainer, mActivity);
+ setupTaskFragmentInfo(overlayContainer, mActivity, true /* isVisible */);
- assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+ assertThat(mSplitController.getAllNonFinishingOverlayContainers())
.containsExactly(overlayContainer);
assertThat(overlayContainer.getTaskId()).isEqualTo(TASK_ID);
assertThat(overlayContainer.areLastRequestedBoundsEqual(bounds)).isTrue();
@@ -304,14 +363,16 @@
}
@Test
- public void testGetTopNonFishingTaskFragmentContainerWithOverlay() {
- final TaskFragmentContainer overlayContainer =
- createTestOverlayContainer(TASK_ID, "test1");
-
- // Add a SplitPinContainer, the overlay should be on top
+ public void testGetTopNonFishingTaskFragmentContainerWithoutAssociatedOverlay() {
final Activity primaryActivity = createMockActivity();
final Activity secondaryActivity = createMockActivity();
-
+ final Rect bounds = new Rect(0, 0, 100, 100);
+ mSplitController.setActivityStackAttributesCalculator(params ->
+ new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
+ final TaskFragmentContainer overlayContainer =
+ createTestOverlayContainer(TASK_ID, "test1", true /* isVisible */,
+ false /* shouldAssociateWithActivity */);
+ overlayContainer.setIsolatedNavigationEnabled(true);
final TaskFragmentContainer primaryContainer =
createMockTaskFragmentContainer(primaryActivity);
final TaskFragmentContainer secondaryContainer =
@@ -353,10 +414,10 @@
@Test
public void testGetTopNonFinishingActivityWithOverlay() {
- TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test1");
-
final Activity activity = createMockActivity();
final TaskFragmentContainer container = createMockTaskFragmentContainer(activity);
+ final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID,
+ "test1");
final TaskContainer task = container.getTaskContainer();
assertThat(task.getTopNonFinishingActivity(true /* includeOverlay */))
@@ -373,8 +434,9 @@
}
@Test
- public void testUpdateOverlayContainer_dismissOverlayIfNeeded() {
- TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test");
+ public void testUpdateOverlayContainer_dismissNonAssociatedOverlayIfNeeded() {
+ TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test",
+ true /* isVisible */, false /* associatedLaunchingActivity */);
mSplitController.updateOverlayContainer(mTransaction, overlayContainer);
@@ -443,11 +505,10 @@
@Test
public void testOnTaskFragmentParentInfoChanged_positionOnlyChange_earlyReturn() {
- final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test");
+ final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test",
+ true /* isVisible */, false /* associatedLaunchingActivity */);
final TaskContainer taskContainer = overlayContainer.getTaskContainer();
- assertThat(taskContainer.getOverlayContainer()).isEqualTo(overlayContainer);
-
spyOn(taskContainer);
final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
@@ -463,16 +524,15 @@
assertWithMessage("The overlay container must still be dismissed even if "
+ "#updateContainer is not called")
- .that(taskContainer.getOverlayContainer()).isNull();
+ .that(taskContainer.getTaskFragmentContainers()).isEmpty();
}
@Test
- public void testOnTaskFragmentParentInfoChanged_invisibleTask_callDismissOverlayContainer() {
- final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test");
+ public void testOnTaskFragmentParentInfoChanged_invisibleTask_callDismissNonAssocOverlay() {
+ final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test",
+ true /* isVisible */, false /* associatedLaunchingActivity */);
final TaskContainer taskContainer = overlayContainer.getTaskContainer();
- assertThat(taskContainer.getOverlayContainer()).isEqualTo(overlayContainer);
-
spyOn(taskContainer);
final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
@@ -487,7 +547,7 @@
assertWithMessage("The overlay container must still be dismissed even if "
+ "#updateContainer is not called")
- .that(taskContainer.getOverlayContainer()).isNull();
+ .that(taskContainer.getTaskFragmentContainers()).isEmpty();
}
@Test
@@ -510,7 +570,7 @@
}
@Test
- public void testApplyActivityStackAttributesForOverlayContainer() {
+ public void testApplyActivityStackAttributesForOverlayContainerAssociatedWithActivity() {
final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, TEST_TAG);
final IBinder token = container.getTaskFragmentToken();
final ActivityStackAttributes attributes = new ActivityStackAttributes.Builder()
@@ -527,7 +587,35 @@
WINDOWING_MODE_MULTI_WINDOW);
verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
TaskFragmentAnimationParams.DEFAULT);
- verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, true);
+ // Set isolated navigation to false if the overlay container is associated with
+ // the launching activity.
+ verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false);
+ verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, true);
+ }
+
+ @Test
+ public void testApplyActivityStackAttributesForOverlayContainerWithoutAssociatedActivity() {
+ final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, TEST_TAG,
+ true, /* isVisible */ false /* associatedWithLaunchingActivity */);
+ final IBinder token = container.getTaskFragmentToken();
+ final ActivityStackAttributes attributes = new ActivityStackAttributes.Builder()
+ .setRelativeBounds(new Rect(0, 0, 200, 200))
+ .setWindowAttributes(new WindowAttributes(DIM_AREA_ON_TASK))
+ .build();
+
+ mSplitPresenter.applyActivityStackAttributes(mTransaction, container,
+ attributes, null /* minDimensions */);
+
+ verify(mSplitPresenter).resizeTaskFragmentIfRegistered(mTransaction, container,
+ attributes.getRelativeBounds());
+ verify(mSplitPresenter).updateTaskFragmentWindowingModeIfRegistered(mTransaction,
+ container, WINDOWING_MODE_MULTI_WINDOW);
+ verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
+ TaskFragmentAnimationParams.DEFAULT);
+ // Set isolated navigation to false if the overlay container is associated with
+ // the launching activity.
+ verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction,
+ container, true);
verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, true);
}
@@ -563,8 +651,7 @@
mSplitPresenter.applyActivityStackAttributes(mTransaction, container, attributes,
new Size(relativeBounds.width() + 1, relativeBounds.height()));
- verify(mSplitPresenter).resizeTaskFragmentIfRegistered(mTransaction, container,
- new Rect());
+ verify(mSplitPresenter).resizeTaskFragmentIfRegistered(mTransaction, container, new Rect());
verify(mSplitPresenter).updateTaskFragmentWindowingModeIfRegistered(mTransaction, container,
WINDOWING_MODE_UNDEFINED);
verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
@@ -573,16 +660,65 @@
verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, false);
}
+ @Test
+ public void testFinishSelfWithActivityIfNeeded() {
+ TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity);
+
+ container.finishSelfWithActivityIfNeeded(mTransaction, mActivity.getActivityToken());
+
+ verify(mSplitPresenter, never()).cleanupContainer(any(), any(), anyBoolean());
+
+ TaskFragmentContainer overlayWithoutAssociation = createTestOverlayContainer(TASK_ID,
+ "test", false /* associateLaunchingActivity */);
+
+ overlayWithoutAssociation.finishSelfWithActivityIfNeeded(mTransaction,
+ mActivity.getActivityToken());
+
+ verify(mSplitPresenter, never()).cleanupContainer(any(), any(), anyBoolean());
+ assertThat(mSplitController.getAllNonFinishingOverlayContainers())
+ .contains(overlayWithoutAssociation);
+
+ TaskFragmentContainer overlayWithAssociation =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test");
+ overlayWithAssociation.setInfo(mTransaction, createMockTaskFragmentInfo(
+ overlayWithAssociation, mActivity, true /* isVisible */));
+ assertThat(mSplitController.getAllNonFinishingOverlayContainers())
+ .contains(overlayWithAssociation);
+ clearInvocations(mSplitPresenter);
+
+ overlayWithAssociation.finishSelfWithActivityIfNeeded(mTransaction, new Binder());
+
+ verify(mSplitPresenter, never()).cleanupContainer(any(), any(), anyBoolean());
+
+ overlayWithAssociation.finishSelfWithActivityIfNeeded(mTransaction,
+ mActivity.getActivityToken());
+
+ verify(mSplitPresenter).cleanupContainer(mTransaction, overlayWithAssociation, false);
+
+ assertThat(mSplitController.getAllNonFinishingOverlayContainers())
+ .doesNotContain(overlayWithAssociation);
+ }
+
/**
- * A simplified version of {@link SplitController.ActivityStartMonitor
- * #createOrUpdateOverlayTaskFragmentIfNeeded}
+ * A simplified version of {@link SplitController#createOrUpdateOverlayTaskFragmentIfNeeded}
*/
@Nullable
private TaskFragmentContainer createOrUpdateOverlayTaskFragmentIfNeeded(@NonNull String tag) {
final Bundle launchOptions = new Bundle();
launchOptions.putString(KEY_OVERLAY_TAG, tag);
+ return createOrUpdateOverlayTaskFragmentIfNeeded(tag, mActivity);
+ }
+
+ /**
+ * A simplified version of {@link SplitController#createOrUpdateOverlayTaskFragmentIfNeeded}
+ */
+ @Nullable
+ private TaskFragmentContainer createOrUpdateOverlayTaskFragmentIfNeeded(
+ @NonNull String tag, @NonNull Activity activity) {
+ final Bundle launchOptions = new Bundle();
+ launchOptions.putString(KEY_OVERLAY_TAG, tag);
return mSplitController.createOrUpdateOverlayTaskFragmentIfNeeded(mTransaction,
- launchOptions, mIntent, mActivity);
+ launchOptions, mIntent, activity);
}
/** Creates a mock TaskFragment that has been registered and appeared in the organizer. */
@@ -590,23 +726,41 @@
private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) {
final TaskFragmentContainer container = mSplitController.newContainer(activity,
activity.getTaskId());
- setupTaskFragmentInfo(container, activity);
+ setupTaskFragmentInfo(container, activity, false /* isVisible */);
return container;
}
@NonNull
private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag) {
+ return createTestOverlayContainer(taskId, tag, false /* isVisible */,
+ true /* associateLaunchingActivity */);
+ }
+
+ @NonNull
+ private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag,
+ boolean isVisible) {
+ return createTestOverlayContainer(taskId, tag, isVisible,
+ true /* associateLaunchingActivity */);
+ }
+
+ // TODO(b/243518738): add more test coverage on overlay container without activity association
+ // once we have use cases.
+ @NonNull
+ private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag,
+ boolean isVisible, boolean associateLaunchingActivity) {
Activity activity = createMockActivity();
TaskFragmentContainer overlayContainer = mSplitController.newContainer(
null /* pendingAppearedActivity */, mIntent, activity, taskId,
- null /* pairedPrimaryContainer */, tag, Bundle.EMPTY);
- setupTaskFragmentInfo(overlayContainer, activity);
+ null /* pairedPrimaryContainer */, tag, Bundle.EMPTY,
+ associateLaunchingActivity);
+ setupTaskFragmentInfo(overlayContainer, activity, isVisible);
return overlayContainer;
}
private void setupTaskFragmentInfo(@NonNull TaskFragmentContainer container,
- @NonNull Activity activity) {
- final TaskFragmentInfo info = createMockTaskFragmentInfo(container, activity);
+ @NonNull Activity activity,
+ boolean isVisible) {
+ final TaskFragmentInfo info = createMockTaskFragmentInfo(container, activity, isVisible);
container.setInfo(mTransaction, info);
mSplitPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), info);
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index c246a19..3441c2b 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -227,13 +227,13 @@
// When the activity is not finishing, do not clear the record.
doReturn(false).when(mActivity).isFinishing();
- mSplitController.onActivityDestroyed(mActivity);
+ mSplitController.onActivityDestroyed(mTransaction, mActivity);
assertTrue(tf.hasActivity(mActivity.getActivityToken()));
// Clear the record when the activity is finishing and destroyed.
doReturn(true).when(mActivity).isFinishing();
- mSplitController.onActivityDestroyed(mActivity);
+ mSplitController.onActivityDestroyed(mTransaction, mActivity);
assertFalse(tf.hasActivity(mActivity.getActivityToken()));
}
@@ -612,7 +612,7 @@
assertFalse(result);
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
- anyString(), any());
+ anyString(), any(), anyBoolean());
}
@Test
@@ -775,7 +775,7 @@
assertTrue(result);
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
- anyString(), any());
+ anyString(), any(), anyBoolean());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
}
@@ -818,7 +818,7 @@
assertTrue(result);
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
- anyString(), any());
+ anyString(), any(), anyBoolean());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
index cc00a49..0af4179 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
@@ -402,7 +402,7 @@
assertTrue(container.hasActivity(mActivity.getActivityToken()));
- taskContainer.onActivityDestroyed(mActivity.getActivityToken());
+ taskContainer.onActivityDestroyed(mTransaction, mActivity.getActivityToken());
// It should not contain the destroyed Activity.
assertFalse(container.hasActivity(mActivity.getActivityToken()));
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index 36d3313..7a98683 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -23,4 +23,12 @@
<uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.WAKEUP_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
+
+ <application>
+ <activity
+ android:name=".desktopmode.DesktopWallpaperActivity"
+ android:excludeFromRecents="true"
+ android:launchMode="singleInstance"
+ android:theme="@style/DesktopWallpaperTheme" />
+ </application>
</manifest>
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
index e73d880..8487e379 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.content.Intent
import android.content.pm.ShortcutInfo
+import android.content.res.Resources
import android.graphics.Insets
import android.graphics.PointF
import android.graphics.Rect
@@ -43,6 +44,9 @@
private lateinit var positioner: BubblePositioner
private val context = ApplicationProvider.getApplicationContext<Context>()
+ private val resources: Resources
+ get() = context.resources
+
private val defaultDeviceConfig =
DeviceConfig(
windowBounds = Rect(0, 0, 1000, 2000),
@@ -205,6 +209,58 @@
}
@Test
+ fun testBubbleBarExpandedViewHeightAndWidth() {
+ val deviceConfig =
+ defaultDeviceConfig.copy(
+ // portrait orientation
+ isLandscape = false,
+ isLargeScreen = true,
+ insets = Insets.of(10, 20, 5, 15),
+ windowBounds = Rect(0, 0, 1800, 2600)
+ )
+ val bubbleBarBounds = Rect(1700, 2500, 1780, 2600)
+
+ positioner.setShowingInBubbleBar(true)
+ positioner.update(deviceConfig)
+ positioner.bubbleBarBounds = bubbleBarBounds
+
+ val spaceBetweenTopInsetAndBubbleBarInLandscape = 1680
+ val expandedViewVerticalSpacing =
+ resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
+ val expectedHeight =
+ spaceBetweenTopInsetAndBubbleBarInLandscape - 2 * expandedViewVerticalSpacing
+ val expectedWidth = resources.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width)
+
+ assertThat(positioner.getExpandedViewWidthForBubbleBar(false)).isEqualTo(expectedWidth)
+ assertThat(positioner.getExpandedViewHeightForBubbleBar(false)).isEqualTo(expectedHeight)
+ }
+
+ @Test
+ fun testBubbleBarExpandedViewHeightAndWidth_screenWidthTooSmall() {
+ val screenWidth = 300
+ val deviceConfig =
+ defaultDeviceConfig.copy(
+ // portrait orientation
+ isLandscape = false,
+ isLargeScreen = true,
+ insets = Insets.of(10, 20, 5, 15),
+ windowBounds = Rect(0, 0, screenWidth, 2600)
+ )
+ val bubbleBarBounds = Rect(100, 2500, 280, 2550)
+ positioner.setShowingInBubbleBar(true)
+ positioner.update(deviceConfig)
+ positioner.bubbleBarBounds = bubbleBarBounds
+
+ val spaceBetweenTopInsetAndBubbleBarInLandscape = 180
+ val expandedViewSpacing =
+ resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
+ val expectedHeight = spaceBetweenTopInsetAndBubbleBarInLandscape - 2 * expandedViewSpacing
+ val expectedWidth = screenWidth - 15 /* horizontal insets */ - 2 * expandedViewSpacing
+ assertThat(positioner.getExpandedViewWidthForBubbleBar(false)).isEqualTo(expectedWidth)
+ assertThat(positioner.getExpandedViewHeightForBubbleBar(false)).isEqualTo(expectedHeight)
+ }
+
+ @Test
fun testGetExpandedViewHeight_max() {
val deviceConfig =
defaultDeviceConfig.copy(
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index ef7478c..c0ff192 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -22,14 +22,15 @@
android:layout_height="wrap_content"
android:gravity="center_horizontal">
- <ImageButton
+ <com.android.wm.shell.windowdecor.HandleImageButton
android:id="@+id/caption_handle"
android:layout_width="@dimen/desktop_mode_fullscreen_decor_caption_width"
android:layout_height="@dimen/desktop_mode_fullscreen_decor_caption_height"
android:paddingVertical="16dp"
+ android:paddingHorizontal="10dp"
android:contentDescription="@string/handle_text"
android:src="@drawable/decor_handle_dark"
tools:tint="@color/desktop_mode_caption_handle_bar_dark"
android:scaleType="fitXY"
- android:background="?android:selectableItemBackground"/>
+ android:background="@android:color/transparent"/>
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ 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 dd6f845..b9ff5c6 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Maak toe"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Maak kieslys toe"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Maak kieslys oop"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimeer skerm"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gryp skerm vas"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 18a4ccf..81ab3ab 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"ዝጋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"ምናሌን ክፈት"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"የማያ ገጹ መጠን አሳድግ"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ማያ ገጹን አሳድግ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 7ca335e..3974c39 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"إغلاق"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"إغلاق القائمة"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"فتح القائمة"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"تكبير الشاشة إلى أقصى حدّ"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"التقاط صورة للشاشة"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 944c4f2..a1ce1b3 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"বন্ধ কৰক"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"মেনু বন্ধ কৰক"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"মেনু খোলক"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্ৰীন মেক্সিমাইজ কৰক"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্ৰীন স্নেপ কৰক"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index c320e41..71dfe5a 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Bağlayın"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menyunu bağlayın"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Menyunu açın"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı maksimum böyüdün"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranı çəkin"</string>
</resources>
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 19ca4d3..f483609 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite meni"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Otvorite meni"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Povećaj ekran"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Uklopi ekran"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 74ae1d7..81d066f 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Закрыць"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрыць меню"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Адкрыць меню"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Разгарнуць на ўвесь экран"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Размясціць на палавіне экрана"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 1b753f5..8f828ba 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Затваряне"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Отваряне на менюто"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Увеличаване на екрана"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Прилепване на екрана"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 2ea22cc..e0a2ea8 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"বন্ধ করুন"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"\'মেনু\' বন্ধ করুন"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"মেনু খুলুন"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্রিন বড় করুন"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্রিনে অ্যাপ মানানসই হিসেবে ছোট বড় করুন"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 13655b3..41c72c1 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Zatvaranje"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvaranje menija"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Otvaranje menija"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snimi ekran"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index cb897c5..6792272 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Tanca"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tanca el menú"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Obre el menú"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximitza la pantalla"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajusta la pantalla"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index ded2707..aafb2e1 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Zavřít"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zavřít nabídku"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Otevřít nabídku"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovat obrazovku"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Rozpůlit obrazovku"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 2bdb29d..8878910 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Luk"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Luk menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Åbn menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimér skærm"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tilpas skærm"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index e99d9d0..7b5c471 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Schließen"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Menü öffnen"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Bildschirm maximieren"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Bildschirm teilen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index d8bb740..14e5e2f 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Κλείσιμο"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Κλείσιμο μενού"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Άνοιγμα μενού"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Μεγιστοποίηση οθόνης"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Προβολή στο μισό της οθόνης"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 5e1b274..7427b62 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 2525b32..cb9ee4f 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close Menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Open Menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximize Screen"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap Screen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 5e1b274..7427b62 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 5e1b274..7427b62 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 0623bef..8498807 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close Menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Open Menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximize Screen"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap Screen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 9fe77dd..406c1f3 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Abrir el menú"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index b88f215..0583d79 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Abrir menú"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 529b6d1..70547f5 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Sule"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Ava menüü"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Kuva täisekraanil"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Kuva poolel ekraanil"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 7438f42..4be35ea 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Itxi"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Itxi menua"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Ireki menua"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Handitu pantaila"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zatitu pantaila"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index f7fcb21..32d5f5f 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"بستن"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"باز کردن منو"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"بزرگ کردن صفحه"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"بزرگ کردن صفحه"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 4001073..6f03545 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Sulje"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Sulje valikko"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Avaa valikko"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Suurenna näyttö"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Jaa näyttö"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index b54f9cf..1fde4cf 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Fermer"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Ouvrir le menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Agrandir l\'écran"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aligner l\'écran"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 357ff91..e7233ae 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Fermer"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Ouvrir le menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mettre en plein écran"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fractionner l\'écran"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index a621907..89db327 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Pechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Pechar o menú"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Abrir menú"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar pantalla"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 43c178f..7e3d7a3 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"બંધ કરો"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"મેનૂ બંધ કરો"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"મેનૂ ખોલો"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"સ્ક્રીન કરો મોટી કરો"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"સ્ક્રીન સ્નૅપ કરો"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 9f6a57f..cd0f4e3 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"बंद करें"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"मेन्यू खोलें"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन को बड़ा करें"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्नैप स्क्रीन"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 4378c56..fc39420 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Otvaranje izbornika"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimalno povećaj zaslon"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Izradi snimku zaslona"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index e5f199f..a8cc5c1 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Bezárás"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü bezárása"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Menü megnyitása"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Képernyő méretének maximalizálása"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Igazodás a képernyő adott részéhez"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index e0a5afe..7f37277 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Փակել"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Փակել ընտրացանկը"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Բացել ընտրացանկը"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ծավալել էկրանը"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ծալել էկրանը"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 8025837..3cf55fa 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Tutup"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Buka Menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Perbesar Layar"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gabungkan Layar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index cece56e..6aa56f9 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Loka"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Loka valmynd"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Opna valmynd"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Stækka skjá"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Smelluskjár"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 731db8c..3c1d5e4 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Chiudi"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Chiudi il menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Apri menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Massimizza schermo"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aggancia schermo"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index adf55f3..a0c3b3a 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"סגירה"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"סגירת התפריט"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"פתיחת התפריט"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"הגדלת המסך"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"כיווץ המסך"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 3543222..fb726c1 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"閉じる"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"メニューを閉じる"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"メニューを開く"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"画面の最大化"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"画面のスナップ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 1e6e657..e9f620a 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"დახურვა"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"მენიუს დახურვა"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"მენიუს გახსნა"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"აპლიკაციის გაშლა სრულ ეკრანზე"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"აპლიკაციის დაპატარავება ეკრანზე"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 6d9ff26..34e4103 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Жабу"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Мәзірді жабу"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Мәзірді ашу"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды ұлғайту"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды бөлу"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 586ef73..362bbad 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"បិទ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"បិទម៉ឺនុយ"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"បើកម៉ឺនុយ"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ពង្រីកអេក្រង់"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ថតអេក្រង់"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 78ca0c7..77cc4a4 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"ಮುಚ್ಚಿ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ಮೆನು ಮುಚ್ಚಿ"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"ಮೆನು ತೆರೆಯಿರಿ"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ಸ್ನ್ಯಾಪ್ ಸ್ಕ್ರೀನ್"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 70aa376..e8b5522 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"닫기"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"메뉴 닫기"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"메뉴 열기"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"화면 최대화"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"화면 분할"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index b2a0a49..7b7779d 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Жабуу"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Менюну жабуу"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Менюну ачуу"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды чоңойтуу"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды сүрөткө тартып алуу"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 1cbdbd4..a351963 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"ປິດ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ປິດເມນູ"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"ເປີດເມນູ"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ປັບຈໍໃຫຍ່ສຸດ"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ສະແນັບໜ້າຈໍ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index d154c57..e4dd739 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Uždaryti"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Uždaryti meniu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Atidaryti meniu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Išskleisti ekraną"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Sutraukti ekraną"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index ce26950..99aebf6 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Aizvērt"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Aizvērt izvēlni"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Atvērt izvēlni"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizēt ekrānu"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fiksēt ekrānu"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 9d69c50..c152c60 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Затворете"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затворете го менито"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Отвори го менито"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Максимизирај го екранот"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Подели го екранот на половина"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index c0e8338..90275cd 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"അടയ്ക്കുക"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"മെനു അടയ്ക്കുക"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"മെനു തുറക്കുക"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"സ്ക്രീൻ വലുതാക്കുക"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"സ്ക്രീൻ സ്നാപ്പ് ചെയ്യുക"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index ba5d283f..4a9fab9 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Хаах"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Цэсийг хаах"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Цэс нээх"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Дэлгэцийг томруулах"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Дэлгэцийг таллах"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 17601c1..5874bff 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"बंद करा"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेनू बंद करा"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"मेनू उघडा"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन मोठी करा"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रीन स्नॅप करा"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index d5547fa..4de8a7b 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Tutup"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Buka Menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimumkan Skrin"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tangkap Skrin"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 07bfc99..5b9e9cb 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"ပိတ်ရန်"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"မီနူး ပိတ်ရန်"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"မီနူး ဖွင့်ရန်"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"စခရင်ကို ချဲ့မည်"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"စခရင်ကို ချုံ့မည်"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index f609d01..9f03d8b 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Lukk"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Åpne menyen"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimer skjermen"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fest skjermen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 9a26b7e..e4830af 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"बन्द गर्नुहोस्"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेनु बन्द गर्नुहोस्"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"मेनु खोल्नुहोस्"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रिन ठुलो बनाउनुहोस्"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रिन स्न्याप गर्नुहोस्"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index a38cb75..0cd27c5 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Sluiten"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menu sluiten"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Menu openen"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Scherm maximaliseren"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Scherm halveren"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index e3097be..bf75185 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ମେନୁ ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"ମେନୁ ଖୋଲନ୍ତୁ"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ସ୍କ୍ରିନକୁ ବଡ଼ କରନ୍ତୁ"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ସ୍କ୍ରିନକୁ ସ୍ନାପ କରନ୍ତୁ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 3aea6f6..325c1e8 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"ਬੰਦ ਕਰੋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ਮੀਨੂ ਬੰਦ ਕਰੋ"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"ਮੀਨੂ ਖੋਲ੍ਹੋ"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ਸਕ੍ਰੀਨ ਦਾ ਆਕਾਰ ਵਧਾਓ"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ਸਕ੍ਰੀਨ ਨੂੰ ਸਨੈਪ ਕਰੋ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index aec3722..a7648c8 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Zamknij"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zamknij menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Otwórz menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksymalizuj ekran"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Przyciągnij ekran"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index ba24d7b..e47d151 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Abrir o menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index f636da79..ff77d3b 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Abrir menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar ecrã"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar ecrã"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index ba24d7b..e47d151 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Abrir o menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index c20f350..ae871f3 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Închide"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Închide meniul"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Deschide meniul"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizează fereastra"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Micșorează fereastra și fixeaz-o"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 49347d2..e23c1ff 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Закрыть"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрыть меню"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Открыть меню"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Развернуть на весь экран"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Свернуть"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index e5a9746..ef1381c 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"වසන්න"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"මෙනුව වසන්න"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"මෙනුව විවෘත කරන්න"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"තිරය උපරිම කරන්න"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ස්නැප් තිරය"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index c2d20dd..55a0312 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Zavrieť"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zavrieť ponuku"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Otvoriť ponuku"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovať obrazovku"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zobraziť polovicu obrazovky"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index cfe4480..bb123dc 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Zapri"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zapri meni"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Odpri meni"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiraj zaslon"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Pripni zaslon"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index cba98c2f..c74a8cd 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Mbyll"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Mbyll menynë"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Hap menynë"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizo ekranin"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Regjistro ekranin"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 5031f5b..0694a97 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Затворите"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затворите мени"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Отворите мени"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Повећај екран"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Уклопи екран"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 742be37..8e0bcfe 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Stäng"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Stäng menyn"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Öppna menyn"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximera skärmen"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fäst skärmen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 68a7262d..41180ab 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Funga"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Funga Menyu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Fungua Menyu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Panua Dirisha kwenye Skrini"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Panga Madirisha kwenye Skrini"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index fe8fa05..01ac78d 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"மூடும்"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"மெனுவை மூடும்"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"மெனுவைத் திற"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"திரையைப் பெரிதாக்கு"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"திரையை ஸ்னாப் செய்"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 9be3f33..6224e72 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"మూసివేయండి"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"మెనూను మూసివేయండి"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"మెనూను తెరవండి"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"స్క్రీన్ సైజ్ను పెంచండి"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"స్క్రీన్ను స్నాప్ చేయండి"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index cd3bf6a..407fbbb 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"ปิด"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ปิดเมนู"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"เปิดเมนู"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ขยายหน้าจอให้ใหญ่สุด"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"สแนปหน้าจอ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index bf05e14..786e99c 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Isara"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Isara ang Menu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Buksan ang Menu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"I-maximize ang Screen"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"I-snap ang Screen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 2dfa38a..e953f58 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Kapat"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menüyü kapat"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Menüyü Aç"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı Büyüt"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranın Yarısına Tuttur"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 57ca64f..fbdf42e 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Закрити"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрити меню"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Відкрити меню"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Розгорнути екран"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Зафіксувати екран"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 0770373..5562fa7 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"بند کریں"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"مینیو بند کریں"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"مینو کھولیں"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"اسکرین کو بڑا کریں"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"اسکرین کا اسناپ شاٹ لیں"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index e2d1f47..50e4232 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Yopish"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menyuni yopish"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Menyuni ochish"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranni yoyish"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranni biriktirish"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 4608b2b..6da8588 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Đóng"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Đóng trình đơn"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Mở Trình đơn"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mở rộng màn hình"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Điều chỉnh kích thước màn hình"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index cbb857c..4318caf 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"关闭"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"关闭菜单"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"打开菜单"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"最大化屏幕"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"屏幕快照"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index d89b2c2..72cd39d 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"關閉"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"打開選單"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 4ce50a4..c06d7b1 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"關閉"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"開啟選單"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index fa680f6..755414e 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -117,4 +117,6 @@
<string name="close_text" msgid="4986518933445178928">"Vala"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Vala Imenyu"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"Vula Imenyu"</string>
+ <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Khulisa Isikrini Sifike Ekugcineni"</string>
+ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Thwebula Isikrini"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 39dd4d3..c2c90c8 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -254,6 +254,8 @@
<dimen name="bubble_bar_expanded_view_caption_dot_size">4dp</dimen>
<!-- The spacing between the dots for the caption menu in the bubble bar expanded view.. -->
<dimen name="bubble_bar_expanded_view_caption_dot_spacing">4dp</dimen>
+ <!-- Width of the expanded bubble bar view shown when the bubble is expanded. -->
+ <dimen name="bubble_bar_expanded_view_width">412dp</dimen>
<!-- Minimum width of the bubble bar manage menu. -->
<dimen name="bubble_bar_manage_menu_min_width">200dp</dimen>
<!-- Size of the dismiss icon in the bubble bar manage menu. -->
@@ -423,8 +425,9 @@
<!-- Height of desktop mode caption for fullscreen tasks. -->
<dimen name="desktop_mode_fullscreen_decor_caption_height">36dp</dimen>
- <!-- Width of desktop mode caption for fullscreen tasks. -->
- <dimen name="desktop_mode_fullscreen_decor_caption_width">128dp</dimen>
+ <!-- Width of desktop mode caption for fullscreen tasks.
+ 80 dp for handle + 20 dp for room to grow on the sides when hovered. -->
+ <dimen name="desktop_mode_fullscreen_decor_caption_width">100dp</dimen>
<!-- Required empty space to be visible for partially offscreen tasks. -->
<dimen name="freeform_required_visible_empty_space_in_header">48dp</dimen>
@@ -512,8 +515,20 @@
<!-- The size of the icon shown in the resize veil. -->
<dimen name="desktop_mode_resize_veil_icon_size">96dp</dimen>
+ <!-- The with of the border around the app task for edge resizing, when
+ enable_windowing_edge_drag_resize is enabled. -->
+ <dimen name="desktop_mode_edge_handle">12dp</dimen>
+
+ <!-- The original width of the border around the app task for edge resizing, when
+ enable_windowing_edge_drag_resize is disabled. -->
<dimen name="freeform_resize_handle">15dp</dimen>
+ <!-- The size of the corner region for drag resizing with touch, when a larger touch region is
+ appropriate. Applied when enable_windowing_edge_drag_resize is enabled. -->
+ <dimen name="desktop_mode_corner_resize_large">48dp</dimen>
+
+ <!-- The original size of the corner region for darg resizing, when
+ enable_windowing_edge_drag_resize is disabled. -->
<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
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 08c2a02..13c0e66 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -23,6 +23,14 @@
<item name="android:windowAnimationStyle">@style/Animation.ForcedResizable</item>
</style>
+ <!-- Theme used for the activity that shows below the desktop mode windows to show wallpaper -->
+ <style name="DesktopWallpaperTheme" parent="@android:style/Theme.Wallpaper.NoTitleBar">
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+ <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ </style>
+
<style name="Animation.ForcedResizable" parent="@android:style/Animation">
<item name="android:activityOpenEnterAnimation">@anim/forced_resizable_enter</item>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
index 1996367..ce0bf8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.animation;
import android.graphics.Path;
+import android.view.animation.BackGestureInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
@@ -95,6 +96,15 @@
public static final PathInterpolator DIM_INTERPOLATOR =
new PathInterpolator(.23f, .87f, .52f, -0.11f);
+ /**
+ * Use this interpolator for animating progress values coming from the back callback to get
+ * the predictive-back-typical decelerate motion.
+ *
+ * This interpolator is similar to {@link Interpolators#STANDARD_DECELERATE} but has a slight
+ * acceleration phase at the start.
+ */
+ public static final Interpolator BACK_GESTURE = new BackGestureInterpolator();
+
// Create the default emphasized interpolator
private static PathInterpolator createEmphasizedInterpolator() {
Path path = new Path();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 73b2656..d3fe4f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -837,6 +837,8 @@
// The next callback should be {@link #onBackAnimationFinished}.
if (mCurrentTracker.getTriggerBack()) {
+ // notify gesture finished
+ mBackNavigationInfo.onBackGestureFinished(true);
dispatchOrAnimateOnBackInvoked(mActiveCallback, mCurrentTracker);
} else {
tryDispatchOnBackCancelled(mActiveCallback);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
index a32b435..4988a94 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
@@ -28,6 +28,7 @@
import android.window.IBackAnimationRunner;
import android.window.IOnBackInvokedCallback;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.Cuj.CujType;
import com.android.wm.shell.common.InteractionJankMonitorUtils;
@@ -108,7 +109,8 @@
}
}
- private boolean shouldMonitorCUJ(RemoteAnimationTarget[] apps) {
+ @VisibleForTesting
+ boolean shouldMonitorCUJ(RemoteAnimationTarget[] apps) {
return apps.length > 0 && mCujType != NO_CUJ;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index 112ed09..7cb5660 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -87,7 +87,7 @@
private val enteringStartOffset =
context.resources.getDimension(R.dimen.cross_activity_back_entering_start_offset)
- private val gestureInterpolator = Interpolators.STANDARD_DECELERATE
+ private val gestureInterpolator = Interpolators.BACK_GESTURE
private val postCommitInterpolator = Interpolators.FAST_OUT_SLOW_IN
private val verticalMoveInterpolator: Interpolator = DecelerateInterpolator()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index c34f30d..ee898a7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -93,7 +93,7 @@
private final PointF mInitialTouchPos = new PointF();
private final Interpolator mPostAnimationInterpolator = Interpolators.EMPHASIZED;
- private final Interpolator mProgressInterpolator = Interpolators.STANDARD_DECELERATE;
+ private final Interpolator mProgressInterpolator = Interpolators.BACK_GESTURE;
private final Interpolator mVerticalMoveInterpolator = new DecelerateInterpolator();
private final Matrix mTransformMatrix = new Matrix();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
index 00daddc..7a6032c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
@@ -28,7 +28,7 @@
private static final String TAG = "ShellBackPreview";
private final SparseArray<BackAnimationRunner> mAnimationDefinition = new SparseArray<>();
- private final ShellBackAnimation mDefaultCrossActivityAnimation;
+ private ShellBackAnimation mDefaultCrossActivityAnimation;
private final ShellBackAnimation mCustomizeActivityAnimation;
private final ShellBackAnimation mCrossTaskAnimation;
@@ -67,10 +67,18 @@
void registerAnimation(
@BackNavigationInfo.BackTargetType int type, @NonNull BackAnimationRunner runner) {
mAnimationDefinition.set(type, runner);
+ // Only happen in test
+ if (BackNavigationInfo.TYPE_CROSS_ACTIVITY == type) {
+ mDefaultCrossActivityAnimation = null;
+ }
}
void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) {
mAnimationDefinition.remove(type);
+ // Only happen in test
+ if (BackNavigationInfo.TYPE_CROSS_ACTIVITY == type) {
+ mDefaultCrossActivityAnimation = null;
+ }
}
/**
@@ -129,9 +137,15 @@
}
void onConfigurationChanged(Configuration newConfig) {
- mCustomizeActivityAnimation.onConfigurationChanged(newConfig);
- mDefaultCrossActivityAnimation.onConfigurationChanged(newConfig);
- mCrossTaskAnimation.onConfigurationChanged(newConfig);
+ if (mCustomizeActivityAnimation != null) {
+ mCustomizeActivityAnimation.onConfigurationChanged(newConfig);
+ }
+ if (mDefaultCrossActivityAnimation != null) {
+ mDefaultCrossActivityAnimation.onConfigurationChanged(newConfig);
+ }
+ if (mCrossTaskAnimation != null) {
+ mCrossTaskAnimation.onConfigurationChanged(newConfig);
+ }
}
BackAnimationRunner getAnimationRunnerAndInit(BackNavigationInfo backNavigationInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 4d5e516..14c3a070 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -149,9 +149,10 @@
mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
if (mShowingInBubbleBar) {
- mExpandedViewLargeScreenWidth = isLandscape()
- ? (int) (bounds.width() * EXPANDED_VIEW_BUBBLE_BAR_LANDSCAPE_WIDTH_PERCENT)
- : (int) (bounds.width() * EXPANDED_VIEW_BUBBLE_BAR_PORTRAIT_WIDTH_PERCENT);
+ mExpandedViewLargeScreenWidth = Math.min(
+ res.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width),
+ mPositionRect.width() - 2 * mExpandedViewPadding
+ );
} else if (mDeviceConfig.isSmallTablet()) {
mExpandedViewLargeScreenWidth = (int) (bounds.width()
* EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT);
@@ -839,11 +840,42 @@
* How tall the expanded view should be when showing from the bubble bar.
*/
public int getExpandedViewHeightForBubbleBar(boolean isOverflow) {
- return isOverflow
- ? mOverflowHeight
- : getExpandedViewBottomForBubbleBar() - mInsets.top - mExpandedViewPadding;
+ if (isOverflow) {
+ return mOverflowHeight;
+ } else {
+ return getBubbleBarExpandedViewHeightForLandscape();
+ }
}
+ /**
+ * Calculate the height of expanded view in landscape mode regardless current orientation.
+ * Here is an explanation:
+ * ------------------------ mScreenRect.top
+ * | top inset ↕ |
+ * |-----------------------
+ * | 16dp spacing ↕ |
+ * | --------- | --- expanded view top
+ * | | | | ↑
+ * | | | | ↓ expanded view height
+ * | --------- | --- expanded view bottom
+ * | 16dp spacing ↕ | ↑
+ * | @bubble bar@ | | height of the bubble bar container
+ * ------------------------ | already includes bottom inset and spacing
+ * | bottom inset ↕ | ↓
+ * |----------------------| --- mScreenRect.bottom
+ */
+ private int getBubbleBarExpandedViewHeightForLandscape() {
+ int heightOfBubbleBarContainer =
+ mScreenRect.height() - getExpandedViewBottomForBubbleBar();
+ // getting landscape height from screen rect
+ int expandedViewHeight = Math.min(mScreenRect.width(), mScreenRect.height());
+ expandedViewHeight -= heightOfBubbleBarContainer; /* removing bubble container height */
+ expandedViewHeight -= mInsets.top; /* removing top inset */
+ expandedViewHeight -= mExpandedViewPadding; /* removing spacing */
+ return expandedViewHeight;
+ }
+
+
/** The bottom position of the expanded view when showing above the bubble bar. */
public int getExpandedViewBottomForBubbleBar() {
return mBubbleBarBounds.top - mExpandedViewPadding;
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 b933e5d..1408ead 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
@@ -59,6 +59,7 @@
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DesktopTasksTransitionObserver;
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
@@ -241,6 +242,7 @@
mainChoreographer,
taskOrganizer,
displayController,
+ rootTaskDisplayAreaOrganizer,
syncQueue,
transitions);
}
@@ -568,6 +570,18 @@
@WMSingleton
@Provides
+ static Optional<DesktopTasksTransitionObserver> provideDesktopTasksTransitionObserver(
+ Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
+ Transitions transitions,
+ ShellInit shellInit
+ ) {
+ return desktopModeTaskRepository.flatMap(repository ->
+ Optional.of(new DesktopTasksTransitionObserver(repository, transitions, shellInit))
+ );
+ }
+
+ @WMSingleton
+ @Provides
static DesktopModeLoggerTransitionObserver provideDesktopModeLoggerTransitionObserver(
ShellInit shellInit,
Transitions transitions,
@@ -622,7 +636,8 @@
@Provides
static Object provideIndependentShellComponentsToCreate(
DragAndDropController dragAndDropController,
- DefaultMixedHandler defaultMixedHandler) {
+ DefaultMixedHandler defaultMixedHandler,
+ Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) {
return new Object();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
index e1e41ee..f1a475a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
@@ -36,7 +36,14 @@
true
}
}
-
+ "moveToNextDisplay" -> {
+ if (!runMoveToNextDisplay(args, pw)) {
+ pw.println("Task not found. Please enter a valid taskId.")
+ false
+ } else {
+ true
+ }
+ }
else -> {
pw.println("Invalid command: ${args[0]}")
false
@@ -61,8 +68,28 @@
return controller.moveToDesktop(taskId, WindowContainerTransaction())
}
+ private fun runMoveToNextDisplay(args: Array<String>, pw: PrintWriter): Boolean {
+ if (args.size < 2) {
+ // First argument is the action name.
+ pw.println("Error: task id should be provided as arguments")
+ return false
+ }
+
+ val taskId = try {
+ args[1].toInt()
+ } catch (e: NumberFormatException) {
+ pw.println("Error: task id should be an integer")
+ return false
+ }
+
+ controller.moveToNextDisplay(taskId)
+ return true
+ }
+
override fun printShellCommandHelp(pw: PrintWriter, prefix: String) {
pw.println("$prefix moveToDesktop <taskId> ")
pw.println("$prefix Move a task with given id to desktop mode.")
+ pw.println("$prefix moveToNextDisplay <taskId> ")
+ pw.println("$prefix Move a task with given id to next display.")
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 120d681..50cea01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -22,6 +22,7 @@
import android.util.ArraySet
import android.util.SparseArray
import android.view.Display.INVALID_DISPLAY
+import android.window.WindowContainerToken
import androidx.core.util.forEach
import androidx.core.util.keyIterator
import androidx.core.util.valueIterator
@@ -49,6 +50,8 @@
var stashed: Boolean = false
)
+ // Token of the current wallpaper activity, used to remove it when the last task is removed
+ var wallpaperActivityToken: WindowContainerToken? = null
// Tasks currently in freeform mode, ordered from top to bottom (top is at index 0).
private val freeformTasksInZOrder = mutableListOf<Int>()
private val activeTasksListeners = ArraySet<ActiveTasksListener>()
@@ -200,6 +203,15 @@
}
/**
+ * Check if a task with the given [taskId] is the only active task on its display
+ */
+ fun isOnlyActiveTask(taskId: Int): Boolean {
+ return displayData.valueIterator().asSequence().any { data ->
+ data.activeTasks.singleOrNull() == taskId
+ }
+ }
+
+ /**
* Get a set of the active tasks for given [displayId]
*/
fun getActiveTasks(displayId: Int): ArraySet<Int> {
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 e210ea7..068661a 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
@@ -40,6 +40,7 @@
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.RemoteTransition
import android.window.TransitionInfo
@@ -381,7 +382,6 @@
)
val wct = WindowContainerTransaction()
exitSplitIfApplicable(wct, taskInfo)
- moveHomeTaskToFront(wct)
bringDesktopAppsToFront(taskInfo.displayId, wct)
addMoveToDesktopChanges(wct, taskInfo)
wct.setBounds(taskInfo.token, freeformBounds)
@@ -401,6 +401,22 @@
shellTaskOrganizer.applyTransaction(wct)
}
+ /**
+ * Perform clean up of the desktop wallpaper activity if the closed window task is
+ * the last active task.
+ *
+ * @param wct transaction to modify if the last active task is closed
+ * @param taskId task id of the window that's being closed
+ */
+ fun onDesktopWindowClose(
+ wct: WindowContainerTransaction,
+ taskId: Int
+ ) {
+ if (desktopModeTaskRepository.isOnlyActiveTask(taskId)) {
+ removeWallpaperActivity(wct)
+ }
+ }
+
/** Move a task with given `taskId` to fullscreen */
fun moveToFullscreen(taskId: Int) {
shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task ->
@@ -588,7 +604,7 @@
if (taskBoundsBeforeMaximize != null) {
destinationBounds.set(taskBoundsBeforeMaximize)
} else {
- getDefaultDesktopTaskBounds(displayLayout, destinationBounds)
+ destinationBounds.set(getDefaultDesktopTaskBounds(displayLayout))
}
} else {
// Save current bounds so that task can be restored back to original bounds if necessary
@@ -624,18 +640,14 @@
}
}
- private fun getDefaultDesktopTaskBounds(displayLayout: DisplayLayout, outBounds: Rect) {
+ private fun getDefaultDesktopTaskBounds(displayLayout: DisplayLayout): Rect {
// TODO(b/319819547): Account for app constraints so apps do not become letterboxed
- val screenBounds = Rect(0, 0, displayLayout.width(), displayLayout.height())
- // Update width and height with default desktop mode values
- val desiredWidth = screenBounds.width().times(DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
- val desiredHeight = screenBounds.height().times(DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
- outBounds.set(0, 0, desiredWidth, desiredHeight)
- // Center the task in screen bounds
- outBounds.offset(
- screenBounds.centerX() - outBounds.centerX(),
- screenBounds.centerY() - outBounds.centerY()
- )
+ val desiredWidth = (displayLayout.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
+ val desiredHeight = (displayLayout.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
+ val heightOffset = (displayLayout.height() - desiredHeight) / 2
+ val widthOffset = (displayLayout.width() - desiredWidth) / 2
+ return Rect(widthOffset, heightOffset,
+ desiredWidth + widthOffset, desiredHeight + heightOffset)
}
private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect {
@@ -680,9 +692,15 @@
KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: bringDesktopAppsToFront")
val activeTasks = desktopModeTaskRepository.getActiveTasks(displayId)
- // First move home to front and then other tasks on top of it
- moveHomeTaskToFront(wct)
+ if (Flags.enableDesktopWindowingWallpaperActivity()) {
+ // Add translucent wallpaper activity to show the wallpaper underneath
+ addWallpaperActivity(wct)
+ } else {
+ // Move home to front
+ moveHomeTaskToFront(wct)
+ }
+ // Then move other tasks on top of it
val allTasksInZOrder = desktopModeTaskRepository.getFreeformTasksInZOrder()
activeTasks
// Sort descending as the top task is at index 0. It should be ordered to top last
@@ -698,6 +716,26 @@
?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) }
}
+ private fun addWallpaperActivity(wct: WindowContainerTransaction) {
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: addWallpaper")
+ val intent = Intent(context, DesktopWallpaperActivity::class.java)
+ val options = ActivityOptions.makeBasic().apply {
+ isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+ pendingIntentBackgroundActivityStartMode =
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ }
+ val pendingIntent = PendingIntent.getActivity(context, /* requestCode = */ 0, intent,
+ PendingIntent.FLAG_IMMUTABLE)
+ wct.sendPendingIntent(pendingIntent, intent, options.toBundle())
+ }
+
+ private fun removeWallpaperActivity(wct: WindowContainerTransaction) {
+ desktopModeTaskRepository.wallpaperActivityToken?.let { token ->
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: removeWallpaper")
+ wct.removeTask(token)
+ }
+ }
+
fun releaseVisualIndicator() {
val t = SurfaceControl.Transaction()
visualIndicator?.releaseVisualIndicator(t)
@@ -745,6 +783,9 @@
reason = "recents animation is running"
false
}
+ // Handle back navigation for the last window if wallpaper available
+ shouldRemoveWallpaper(request) ->
+ true
// Only handle open or to front transitions
request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> {
reason = "transition type not handled (${request.type})"
@@ -781,6 +822,7 @@
val result = triggerTask?.let { task ->
when {
+ request.type == TRANSIT_TO_BACK -> handleBackNavigation(task)
// If display has tasks stashed, handle as stashed launch
task.isStashed -> handleStashedTaskLaunch(task)
// Check if the task has a top transparent activity
@@ -828,6 +870,14 @@
return Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)
}
+ private fun shouldRemoveWallpaper(request: TransitionRequestInfo): Boolean {
+ return Flags.enableDesktopWindowingWallpaperActivity() &&
+ request.type == TRANSIT_TO_BACK &&
+ request.triggerTask?.let { task ->
+ desktopModeTaskRepository.isOnlyActiveTask(task.taskId)
+ } ?: false
+ }
+
private fun handleFreeformTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch")
val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
@@ -885,12 +935,26 @@
}
}
+ /** Handle back navigation by removing wallpaper activity if it's the last active task */
+ private fun handleBackNavigation(task: RunningTaskInfo): WindowContainerTransaction? {
+ if (desktopModeTaskRepository.isOnlyActiveTask(task.taskId) &&
+ desktopModeTaskRepository.wallpaperActivityToken != null) {
+ // Remove wallpaper activity when the last active task is removed
+ return WindowContainerTransaction().also { wct ->
+ removeWallpaperActivity(wct)
+ }
+ } else {
+ return null
+ }
+ }
+
private fun addMoveToDesktopChanges(
wct: WindowContainerTransaction,
taskInfo: RunningTaskInfo
) {
- val displayWindowingMode = taskInfo.configuration.windowConfiguration.displayWindowingMode
- val targetWindowingMode = if (displayWindowingMode == WINDOWING_MODE_FREEFORM) {
+ val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
+ val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
+ val targetWindowingMode = if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) {
// Display windowing is freeform, set to undefined and inherit it
WINDOWING_MODE_UNDEFINED
} else {
@@ -907,8 +971,9 @@
wct: WindowContainerTransaction,
taskInfo: RunningTaskInfo
) {
- val displayWindowingMode = taskInfo.configuration.windowConfiguration.displayWindowingMode
- val targetWindowingMode = if (displayWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
+ val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
+ val targetWindowingMode = if (tdaWindowingMode == WINDOWING_MODE_FULLSCREEN) {
// Display windowing is fullscreen, set to undefined and inherit it
WINDOWING_MODE_UNDEFINED
} else {
@@ -1090,17 +1155,14 @@
* @param taskInfo the task being dragged.
* @param y height of drag, to be checked against status bar height.
*/
- fun onDragPositioningEndThroughStatusBar(
- inputCoordinates: PointF,
- taskInfo: RunningTaskInfo,
- freeformBounds: Rect
- ) {
+ fun onDragPositioningEndThroughStatusBar(inputCoordinates: PointF, taskInfo: RunningTaskInfo) {
val indicator = visualIndicator ?: return
val indicatorType = indicator
.updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
when (indicatorType) {
DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> {
- finalizeDragToDesktop(taskInfo, freeformBounds)
+ val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
+ finalizeDragToDesktop(taskInfo, getDefaultDesktopTaskBounds(displayLayout))
}
DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR,
DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> {
@@ -1354,6 +1416,13 @@
"setTaskListener"
) { _ -> listener?.let { remoteListener.register(it) } ?: remoteListener.unregister() }
}
+
+ override fun moveToDesktop(taskId: Int) {
+ ExecutorUtils.executeRemoteCallWithTaskPermission(
+ controller,
+ "moveToDesktop"
+ ) { c -> c.moveToDesktop(taskId) }
+ }
}
companion object {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
new file mode 100644
index 0000000..20df264
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.os.IBinder
+import android.view.SurfaceControl
+import android.view.WindowManager
+import android.window.TransitionInfo
+import com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.util.KtProtoLog
+
+/**
+ * A [Transitions.TransitionObserver] that observes shell transitions and updates
+ * the [DesktopModeTaskRepository] state TODO: b/332682201
+ * This observes transitions related to desktop mode
+ * and other transitions that originate both within and outside shell.
+ */
+class DesktopTasksTransitionObserver(
+ private val desktopModeTaskRepository: DesktopModeTaskRepository,
+ private val transitions: Transitions,
+ shellInit: ShellInit
+) : Transitions.TransitionObserver {
+
+ init {
+ if (Transitions.ENABLE_SHELL_TRANSITIONS && DesktopModeStatus.isEnabled()) {
+ shellInit.addInitCallback(::onInit, this)
+ }
+ }
+
+ fun onInit() {
+ KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTasksTransitionObserver: onInit")
+ transitions.registerObserver(this)
+ }
+
+ override fun onTransitionReady(
+ transition: IBinder,
+ info: TransitionInfo,
+ startTransaction: SurfaceControl.Transaction,
+ finishTransaction: SurfaceControl.Transaction
+ ) {
+ // TODO: b/332682201 Update repository state
+ updateWallpaperToken(info)
+ }
+
+ override fun onTransitionStarting(transition: IBinder) {
+ // TODO: b/332682201 Update repository state
+ }
+
+ override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
+ // TODO: b/332682201 Update repository state
+ }
+
+ override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
+ // TODO: b/332682201 Update repository state
+ }
+
+ private fun updateWallpaperToken(info: TransitionInfo) {
+ if (!enableDesktopWindowingWallpaperActivity()) {
+ return
+ }
+ info.changes.forEach { change ->
+ change.taskInfo?.let { taskInfo ->
+ if (DesktopWallpaperActivity.isWallpaperTask(taskInfo)) {
+ when (change.mode) {
+ WindowManager.TRANSIT_OPEN ->
+ desktopModeTaskRepository.wallpaperActivityToken = taskInfo.token
+ WindowManager.TRANSIT_CLOSE ->
+ desktopModeTaskRepository.wallpaperActivityToken = null
+ else -> {}
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt
new file mode 100644
index 0000000..c4a4474
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.Activity
+import android.app.ActivityManager
+import android.content.ComponentName
+import android.os.Bundle
+import android.view.WindowManager
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.util.KtProtoLog
+
+/**
+ * A transparent activity used in the desktop mode to show the wallpaper under the freeform windows.
+ * This activity will be running in `FULLSCREEN` windowing mode, which ensures it hides Launcher.
+ * When entering desktop, we would ensure that it's added behind desktop apps and removed when
+ * leaving the desktop mode.
+ *
+ * Note! This activity should NOT interact directly with any other code in the Shell without calling
+ * onto the shell main thread. Activities are always started on the main thread.
+ */
+class DesktopWallpaperActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopWallpaperActivity: onCreate")
+ super.onCreate(savedInstanceState)
+ window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
+ }
+
+ companion object {
+ private const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
+ private val wallpaperActivityComponent =
+ ComponentName(SYSTEM_UI_PACKAGE_NAME, DesktopWallpaperActivity::class.java.name)
+
+ @JvmStatic
+ fun isWallpaperTask(taskInfo: ActivityManager.RunningTaskInfo) =
+ taskInfo.baseIntent.component?.let(::isWallpaperComponent) ?: false
+
+ @JvmStatic
+ fun isWallpaperComponent(component: ComponentName) =
+ component == wallpaperActivityComponent
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 6bdaf1e..fa43522 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -45,4 +45,7 @@
/** Set listener that will receive callbacks about updates to desktop tasks */
oneway void setTaskListener(IDesktopTaskListener listener);
+
+ /** Move a task with given `taskId` to desktop */
+ void moveToDesktop(int taskId);
}
\ No newline at end of file
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 c16eac8..57cf992 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
@@ -96,6 +96,7 @@
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
@@ -522,9 +523,27 @@
mTaskOrganizer.reparentChildSurfaceToTask(taskId, overlay, t);
t.setLayer(overlay, Integer.MAX_VALUE);
t.apply();
+ // This serves as a last resort in case the Shell Transition is not handled properly.
+ // We want to make sure the overlay passed from Launcher gets removed eventually.
+ mayRemoveContentOverlay(overlay);
}
}
+ private void mayRemoveContentOverlay(SurfaceControl overlay) {
+ final WeakReference<SurfaceControl> overlayRef = new WeakReference<>(overlay);
+ final long timeoutDuration = (mEnterAnimationDuration
+ + CONTENT_OVERLAY_FADE_OUT_DELAY_MS
+ + EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS) * 2L;
+ mMainExecutor.executeDelayed(() -> {
+ final SurfaceControl overlayLeash = overlayRef.get();
+ if (overlayLeash != null && overlayLeash.isValid() && overlayLeash == mPipOverlay) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "Cleanup the overlay(%s) as a last resort.", overlayLeash);
+ removeContentOverlay(overlayLeash, null /* callback */);
+ }
+ }, timeoutDuration);
+ }
+
/**
* Callback when launcher aborts swipe-pip-to-home operation.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index b179b5b..a454d48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -159,7 +159,7 @@
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (transition == mEnterTransition) {
+ if (transition == mEnterTransition || info.getType() == TRANSIT_PIP) {
mEnterTransition = null;
if (mPipScheduler.isInSwipePipToHomeTransition()) {
// If this is the second transition as a part of swipe PiP to home cuj,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
index e8f58fe..62d195e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
@@ -37,4 +37,9 @@
* Called when a running task vanishes.
*/
void onRunningTaskVanished(in RunningTaskInfo taskInfo);
+
+ /**
+ * Called when a running task changes.
+ */
+ void onRunningTaskChanged(in RunningTaskInfo taskInfo);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index eebd133..77b8663 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -16,6 +16,9 @@
package com.android.wm.shell.recents;
+import android.annotation.Nullable;
+import android.graphics.Color;
+
import com.android.wm.shell.shared.annotations.ExternalThread;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
@@ -40,4 +43,12 @@
*/
default void addAnimationStateListener(Executor listenerExecutor, Consumer<Boolean> listener) {
}
+
+ /**
+ * Sets a background color on the transition root layered behind the outgoing task. {@code null}
+ * may be used to clear any previously set colors to avoid showing a background at all. The
+ * color is always shown at full opacity.
+ */
+ default void setTransitionBackgroundColor(@Nullable Color color) {
+ }
}
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 f9fcfac..e7d9812 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
@@ -19,6 +19,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.pm.PackageManager.FEATURE_PC;
+import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
@@ -26,10 +27,10 @@
import android.app.ActivityTaskManager;
import android.app.IApplicationThread;
import android.app.PendingIntent;
-import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Color;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Slog;
@@ -86,7 +87,7 @@
private final ActivityTaskManager mActivityTaskManager;
private RecentsTransitionHandler mTransitionHandler = null;
private IRecentTasksListener mListener;
- private final boolean mIsDesktopMode;
+ private final boolean mPcFeatureEnabled;
// Mapping of split task ids, mappings are symmetrical (ie. if t1 is the taskid of a task in a
// pair, then mSplitTasks[t1] = t2, and mSplitTasks[t2] = t1)
@@ -133,7 +134,7 @@
mShellController = shellController;
mShellCommandHandler = shellCommandHandler;
mActivityTaskManager = activityTaskManager;
- mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
+ mPcFeatureEnabled = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mTaskStackListener = taskStackListener;
mDesktopModeTaskRepository = desktopModeTaskRepository;
mMainExecutor = mainExecutor;
@@ -252,8 +253,10 @@
notifyRunningTaskVanished(taskInfo);
}
- public void onTaskWindowingModeChanged(TaskInfo taskInfo) {
+ /** Notify listeners that the windowing mode of the given Task was updated. */
+ public void onTaskWindowingModeChanged(ActivityManager.RunningTaskInfo taskInfo) {
notifyRecentTasksChanged();
+ notifyRunningTaskChanged(taskInfo);
}
@Override
@@ -278,7 +281,9 @@
* Notify the running task listener that a task appeared on desktop environment.
*/
private void notifyRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
- if (mListener == null || !mIsDesktopMode || taskInfo.realActivity == null) {
+ if (mListener == null
+ || !shouldEnableRunningTasksForDesktopMode()
+ || taskInfo.realActivity == null) {
return;
}
try {
@@ -292,7 +297,9 @@
* Notify the running task listener that a task was removed on desktop environment.
*/
private void notifyRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- if (mListener == null || !mIsDesktopMode || taskInfo.realActivity == null) {
+ if (mListener == null
+ || !shouldEnableRunningTasksForDesktopMode()
+ || taskInfo.realActivity == null) {
return;
}
try {
@@ -302,6 +309,27 @@
}
}
+ /**
+ * Notify the running task listener that a task was changed on desktop environment.
+ */
+ private void notifyRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mListener == null
+ || !shouldEnableRunningTasksForDesktopMode()
+ || taskInfo.realActivity == null) {
+ return;
+ }
+ try {
+ mListener.onRunningTaskChanged(taskInfo);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed call onRunningTaskChanged", e);
+ }
+ }
+
+ private boolean shouldEnableRunningTasksForDesktopMode() {
+ return mPcFeatureEnabled
+ || (DesktopModeStatus.isEnabled() && enableDesktopWindowingTaskbarRunningApps());
+ }
+
@VisibleForTesting
void registerRecentTasksListener(IRecentTasksListener listener) {
mListener = listener;
@@ -449,6 +477,16 @@
});
});
}
+
+ @Override
+ public void setTransitionBackgroundColor(@Nullable Color color) {
+ mMainExecutor.execute(() -> {
+ if (mTransitionHandler == null) {
+ return;
+ }
+ mTransitionHandler.setTransitionBackgroundColor(color);
+ });
+ }
}
@@ -476,6 +514,11 @@
public void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
mListener.call(l -> l.onRunningTaskVanished(taskInfo));
}
+
+ @Override
+ public void onRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ mListener.call(l -> l.onRunningTaskChanged(taskInfo));
+ }
};
public IRecentTasksImpl(RecentTasksController controller) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 3b4fb9f..c625b69 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -22,6 +22,7 @@
import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
+import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
@@ -35,6 +36,7 @@
import android.app.IApplicationThread;
import android.app.PendingIntent;
import android.content.Intent;
+import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
@@ -55,10 +57,13 @@
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.sysui.ShellInit;
@@ -90,6 +95,7 @@
private final ArrayList<RecentsMixedHandler> mMixers = new ArrayList<>();
private final HomeTransitionObserver mHomeTransitionObserver;
+ private @Nullable Color mBackgroundColor;
public RecentsTransitionHandler(ShellInit shellInit, Transitions transitions,
@Nullable RecentTasksController recentTasksController,
@@ -121,6 +127,15 @@
mStateListeners.add(listener);
}
+ /**
+ * Sets a background color on the transition root layered behind the outgoing task. {@code null}
+ * may be used to clear any previously set colors to avoid showing a background at all. The
+ * color is always shown at full opacity.
+ */
+ public void setTransitionBackgroundColor(@Nullable Color color) {
+ mBackgroundColor = color;
+ }
+
@VisibleForTesting
public IBinder startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
IApplicationThread appThread, IRecentsAnimationRunner listener) {
@@ -467,6 +482,16 @@
final int belowLayers = info.getChanges().size();
final int middleLayers = info.getChanges().size() * 2;
final int aboveLayers = info.getChanges().size() * 3;
+
+ // Add a background color to each transition root in this transition.
+ if (mBackgroundColor != null) {
+ info.getChanges().stream()
+ .mapToInt((change) -> TransitionUtil.rootIndexFor(change, info))
+ .distinct()
+ .mapToObj((rootIndex) -> info.getRoot(rootIndex).getLeash())
+ .forEach((root) -> createBackgroundSurface(t, root, middleLayers));
+ }
+
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
@@ -1023,13 +1048,16 @@
}
if (mPipTransaction != null && sendUserLeaveHint) {
SurfaceControl pipLeash = null;
+ TransitionInfo.Change pipChange = null;
if (mPipTask != null) {
- pipLeash = mInfo.getChange(mPipTask).getLeash();
+ pipChange = mInfo.getChange(mPipTask);
+ pipLeash = pipChange.getLeash();
} else if (mPipTaskId != -1) {
// find a task with taskId from #setFinishTaskTransaction()
for (TransitionInfo.Change change : mInfo.getChanges()) {
if (change.getTaskInfo() != null
&& change.getTaskInfo().taskId == mPipTaskId) {
+ pipChange = change;
pipLeash = change.getLeash();
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"RecentsController.finishInner:"
@@ -1048,6 +1076,28 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"RecentsController.finishInner: PiP transaction %s merged",
mPipTransaction);
+ if (PipUtils.isPip2ExperimentEnabled()) {
+ // If this path is triggered, we are in auto-enter PiP flow in gesture
+ // navigation mode, which means "Recents" transition should be followed
+ // by a TRANSIT_PIP. Hence, we take the WCT was about to be sent
+ // to Core to be applied during finishTransition(), we modify it to
+ // factor in PiP changes, and we send it as a direct startWCT for
+ // a new TRANSIT_PIP type transition. Recents still sends
+ // finishTransition() to update visibilities, but with finishWCT=null.
+ TransitionRequestInfo requestInfo = new TransitionRequestInfo(
+ TRANSIT_PIP, null /* triggerTask */, pipChange.getTaskInfo(),
+ null /* remote */, null /* displayChange */, 0 /* flags */);
+ // Use mTransition IBinder token temporarily just to get PipTransition
+ // to return from its handleRequest(). The actual TRANSIT_PIP will have
+ // anew token once it arrives into PipTransition#startAnimation().
+ Pair<Transitions.TransitionHandler, WindowContainerTransaction>
+ requestRes = mTransitions.dispatchRequest(mTransition,
+ requestInfo, null /* skip */);
+ wct.merge(requestRes.second, true);
+ mTransitions.startTransition(TRANSIT_PIP, wct, null /* handler */);
+ // We need to clear the WCT to send finishWCT=null for Recents.
+ wct.clear();
+ }
}
mPipTaskId = -1;
mPipTask = null;
@@ -1080,6 +1130,29 @@
return true;
}
+ private void createBackgroundSurface(SurfaceControl.Transaction transaction,
+ SurfaceControl parent, int layer) {
+ if (mBackgroundColor == null) {
+ return;
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " adding background color to layer=%d", layer);
+ final SurfaceControl background = new SurfaceControl.Builder()
+ .setName("recents_background")
+ .setColorLayer()
+ .setOpaque(true)
+ .setParent(parent)
+ .build();
+ transaction.setColor(background, colorToFloatArray(mBackgroundColor));
+ transaction.setLayer(background, layer);
+ transaction.setAlpha(background, 1F);
+ transaction.show(background);
+ }
+
+ private static float[] colorToFloatArray(@NonNull Color color) {
+ return new float[]{color.red(), color.green(), color.blue()};
+ }
+
private void cleanUpPausingOrClosingTask(TaskState task, WindowContainerTransaction wct,
SurfaceControl.Transaction finishTransaction, boolean sendUserLeaveHint) {
if (!sendUserLeaveHint && task.isLeaf()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 74e85f8..9adb67c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -507,6 +507,15 @@
final Point animRelOffset = new Point(
change.getEndAbsBounds().left - animRoot.getOffset().x,
change.getEndAbsBounds().top - animRoot.getOffset().y);
+
+ if (change.getActivityComponent() != null) {
+ // For appcompat letterbox: we intentionally report the task-bounds so that we
+ // can animate as-if letterboxes are "part of" the activity. This means we can't
+ // always rely solely on endAbsBounds and need to also max with endRelOffset.
+ animRelOffset.x = Math.max(animRelOffset.x, change.getEndRelOffset().x);
+ animRelOffset.y = Math.max(animRelOffset.y, change.getEndRelOffset().y);
+ }
+
if (change.getActivityComponent() != null && !isActivityLevel) {
// At this point, this is an independent activity change in a non-activity
// transition. This means that an activity transition got erroneously combined
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index c9185ae..b1a1e59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -20,6 +20,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
import static com.android.wm.shell.transition.Transitions.TransitionObserver;
import android.annotation.NonNull;
@@ -60,7 +61,8 @@
@NonNull SurfaceControl.Transaction finishTransaction) {
for (TransitionInfo.Change change : info.getChanges()) {
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo == null
+ if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+ || taskInfo == null
|| taskInfo.displayId != DEFAULT_DISPLAY
|| taskInfo.taskId == -1
|| !taskInfo.isRunning) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index c26604a..7c2ba45 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -293,7 +293,13 @@
@Override
public void onFoldStateChanged(boolean isFolded) {
if (isFolded) {
+ // Reset unfold animation finished flag on folding, so it could be used next time
+ // when we unfold the device as an indication that animation hasn't finished yet
mAnimationFinished = false;
+
+ // If we are currently animating unfold animation we should finish it because
+ // the animation might not start and finish as the device was folded
+ finishTransitionIfNeeded();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index c59a1b4..87dc391 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -19,23 +19,30 @@
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.content.pm.PackageManager.FEATURE_PC;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.view.WindowManager.TRANSIT_CHANGE;
import android.app.ActivityManager.RunningTaskInfo;
+import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
+import android.provider.Settings;
import android.util.SparseArray;
import android.view.Choreographer;
+import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
+import android.window.DisplayAreaInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
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.SyncTransactionQueue;
@@ -53,6 +60,7 @@
private final Handler mMainHandler;
private final Choreographer mMainChoreographer;
private final DisplayController mDisplayController;
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private final SyncTransactionQueue mSyncQueue;
private final Transitions mTransitions;
private TaskOperations mTaskOperations;
@@ -65,6 +73,7 @@
Choreographer mainChoreographer,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
SyncTransactionQueue syncQueue,
Transitions transitions) {
mContext = context;
@@ -72,6 +81,7 @@
mMainChoreographer = mainChoreographer;
mTaskOrganizer = taskOrganizer;
mDisplayController = displayController;
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mSyncQueue = syncQueue;
mTransitions = transitions;
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -158,10 +168,33 @@
}
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
- return taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
- || (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
- && taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
- == WINDOWING_MODE_FREEFORM);
+ if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ return true;
+ }
+ if (taskInfo.getActivityType() != ACTIVITY_TYPE_STANDARD) {
+ return false;
+ }
+ final DisplayAreaInfo rootDisplayAreaInfo =
+ mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId);
+ if (rootDisplayAreaInfo != null) {
+ return rootDisplayAreaInfo.configuration.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_FREEFORM;
+ }
+
+ // It is possible that the rootDisplayAreaInfo is null when a task appears soon enough after
+ // a new display shows up, because TDA may appear after task appears in WM shell. Instead of
+ // fixing the synchronization issues, let's use other signals to "guess" the answer. It is
+ // OK in this context because no other captions other than the legacy developer option
+ // freeform and Kingyo/CF PC may use this class. WM shell should have full control over the
+ // condition where captions should show up in all new cases such as desktop mode, for which
+ // we should use different window decor view models. Ultimately Kingyo/CF PC may need to
+ // spin up their own window decor view model when they start to care about multiple
+ // displays.
+ if (isPc()) {
+ return true;
+ }
+ return taskInfo.displayId != Display.DEFAULT_DISPLAY
+ && forcesDesktopModeOnExternalDisplays();
}
private void createWindowDecoration(
@@ -231,7 +264,10 @@
mTaskOperations.minimizeTask(mTaskToken);
} else if (id == R.id.maximize_window) {
RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
- mTaskOperations.maximizeTask(taskInfo);
+ final DisplayAreaInfo rootDisplayAreaInfo =
+ mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId);
+ mTaskOperations.maximizeTask(taskInfo,
+ rootDisplayAreaInfo.configuration.windowConfiguration.getWindowingMode());
}
}
@@ -305,4 +341,17 @@
return true;
}
}
+
+ /**
+ * Returns if this device is a PC.
+ */
+ private boolean isPc() {
+ return mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
+ }
+
+ private boolean forcesDesktopModeOnExternalDisplays() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ return Settings.Global.getInt(resolver,
+ DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0;
+ }
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index beead6a..43fd32b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -16,17 +16,23 @@
package com.android.wm.shell.windowdecor;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
+
import android.annotation.NonNull;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.VectorDrawable;
import android.os.Handler;
+import android.util.Size;
import android.view.Choreographer;
import android.view.SurfaceControl;
import android.view.View;
@@ -222,7 +228,6 @@
mHandler,
mChoreographer,
mDisplay.getDisplayId(),
- 0 /* taskCornerRadius */,
mDecorationContainerSurface,
mDragPositioningCallback,
mSurfaceControlBuilderSupplier,
@@ -234,12 +239,10 @@
.getScaledTouchSlop();
mDragDetector.setTouchSlop(touchSlop);
- final int resize_handle = mResult.mRootView.getResources()
- .getDimensionPixelSize(R.dimen.freeform_resize_handle);
- final int resize_corner = mResult.mRootView.getResources()
- .getDimensionPixelSize(R.dimen.freeform_resize_corner);
- mDragResizeListener.setGeometry(
- mResult.mWidth, mResult.mHeight, resize_handle, resize_corner, touchSlop);
+ final Resources res = mResult.mRootView.getResources();
+ mDragResizeListener.setGeometry(new DragResizeWindowGeometry(0 /* taskCornerRadius */,
+ new Size(mResult.mWidth, mResult.mHeight), getResizeEdgeHandleSize(res),
+ getFineResizeCornerSize(res), getLargeResizeCornerSize(res)), touchSlop);
}
/**
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 a0f9c6b..777ab9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -84,6 +84,7 @@
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
+import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
@@ -407,7 +408,9 @@
mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId,
SplitScreenController.EXIT_REASON_DESKTOP_MODE);
} else {
- mTaskOperations.closeTask(mTaskToken);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mDesktopTasksController.onDesktopWindowClose(wct, mTaskId);
+ mTaskOperations.closeTask(mTaskToken, wct);
}
} else if (id == R.id.back_button) {
mTaskOperations.injectBackKey();
@@ -858,10 +861,7 @@
// as it likely will change.
relevantDecor.updateHoverAndPressStatus(ev);
mDesktopTasksController.onDragPositioningEndThroughStatusBar(
- new PointF(ev.getRawX(), ev.getRawY()),
- relevantDecor.mTaskInfo,
- calculateFreeformBounds(ev.getDisplayId(),
- DesktopTasksController.DESKTOP_MODE_INITIAL_BOUNDS_SCALE));
+ new PointF(ev.getRawX(), ev.getRawY()), relevantDecor.mTaskInfo);
mMoveToDesktopAnimator = null;
return;
} else {
@@ -913,23 +913,6 @@
}
}
- /**
- * Gets bounds of a scaled window centered relative to the screen bounds
- * @param scale the amount to scale to relative to the Screen Bounds
- */
- private Rect calculateFreeformBounds(int displayId, float scale) {
- // TODO(b/319819547): Account for app constraints so apps do not become letterboxed
- final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(displayId);
- final int screenWidth = displayLayout.width();
- final int screenHeight = displayLayout.height();
-
- final float adjustmentPercentage = (1f - scale) / 2;
- return new Rect((int) (screenWidth * adjustmentPercentage),
- (int) (screenHeight * adjustmentPercentage),
- (int) (screenWidth * (adjustmentPercentage + scale)),
- (int) (screenHeight * (adjustmentPercentage + scale)));
- }
-
@Nullable
private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) {
final DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
@@ -1025,6 +1008,7 @@
return false;
}
return DesktopModeStatus.isEnabled()
+ && !DesktopWallpaperActivity.isWallpaperTask(taskInfo)
&& taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
&& taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
&& !taskInfo.configuration.windowConfiguration.isAlwaysOnTop()
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 d0879434..2bbe530 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
@@ -19,15 +19,20 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.windowingModeToString;
+import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
@@ -39,6 +44,8 @@
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.util.Log;
+import android.util.Size;
import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.SurfaceControl;
@@ -273,7 +280,6 @@
mHandler,
mChoreographer,
mDisplay.getDisplayId(),
- mRelayoutParams.mCornerRadius,
mDecorationContainerSurface,
mDragPositioningCallback,
mSurfaceControlBuilderSupplier,
@@ -285,15 +291,13 @@
.getScaledTouchSlop();
mDragDetector.setTouchSlop(touchSlop);
- final int resize_handle = mResult.mRootView.getResources()
- .getDimensionPixelSize(R.dimen.freeform_resize_handle);
- final int resize_corner = mResult.mRootView.getResources()
- .getDimensionPixelSize(R.dimen.freeform_resize_corner);
-
// If either task geometry or position have changed, update this task's
// exclusion region listener
+ final Resources res = mResult.mRootView.getResources();
if (mDragResizeListener.setGeometry(
- mResult.mWidth, mResult.mHeight, resize_handle, resize_corner, touchSlop)
+ new DragResizeWindowGeometry(mRelayoutParams.mCornerRadius,
+ new Size(mResult.mWidth, mResult.mHeight), getResizeEdgeHandleSize(res),
+ getFineResizeCornerSize(res), getLargeResizeCornerSize(res)), touchSlop)
|| !mTaskInfo.positionInParent.equals(mPositionInParent)) {
updateExclusionRegion();
}
@@ -424,7 +428,7 @@
return mHandleMenu != null;
}
- boolean shouldResizeListenerHandleEvent(MotionEvent e, Point offset) {
+ boolean shouldResizeListenerHandleEvent(@NonNull MotionEvent e, @NonNull Point offset) {
return mDragResizeListener != null && mDragResizeListener.shouldHandleEvent(e, offset);
}
@@ -433,15 +437,20 @@
}
private void loadAppInfo() {
+ final ActivityInfo activityInfo = mTaskInfo.topActivityInfo;
+ if (activityInfo == null) {
+ Log.e(TAG, "Top activity info not found in task");
+ return;
+ }
PackageManager pm = mContext.getApplicationContext().getPackageManager();
final IconProvider provider = new IconProvider(mContext);
- mAppIconDrawable = provider.getIcon(mTaskInfo.topActivityInfo);
+ mAppIconDrawable = provider.getIcon(activityInfo);
final Resources resources = mContext.getResources();
final BaseIconFactory factory = new BaseIconFactory(mContext,
resources.getDisplayMetrics().densityDpi,
resources.getDimensionPixelSize(R.dimen.desktop_mode_caption_icon_radius));
mAppIconBitmap = factory.createScaledBitmap(mAppIconDrawable, MODE_DEFAULT);
- final ApplicationInfo applicationInfo = mTaskInfo.topActivityInfo.applicationInfo;
+ final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
mAppName = pm.getApplicationLabel(applicationInfo);
}
@@ -752,7 +761,9 @@
final int action = ev.getActionMasked();
// The comparison against ACTION_UP is needed for the cancel drag to desktop case.
handle.setHovered(inHandle && action != ACTION_UP);
- handle.setPressed(inHandle && action == ACTION_DOWN);
+ // We want handle to remain pressed if the pointer moves outside of it during a drag.
+ handle.setPressed((inHandle && action == ACTION_DOWN)
+ || (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL));
if (isHandleMenuActive()) {
mHandleMenu.checkMotionEvent(ev);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
index 8ce2d6d..421ffd9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
@@ -23,6 +23,9 @@
* Callback called when receiving drag-resize or drag-move related input events.
*/
public interface DragPositioningCallback {
+ /**
+ * Indicates the direction of resizing. May be combined together to indicate a diagonal drag.
+ */
@IntDef(flag = true, value = {
CTRL_TYPE_UNDEFINED, CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM
})
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 97eb4a4..9624d46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -30,6 +30,7 @@
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
@@ -39,6 +40,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Size;
import android.view.Choreographer;
import android.view.IWindowSession;
import android.view.InputChannel;
@@ -55,6 +57,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
+import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -66,40 +69,20 @@
class DragResizeInputListener implements AutoCloseable {
private static final String TAG = "DragResizeInputListener";
private final IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();
- private final Context mContext;
- private final Handler mHandler;
- private final Choreographer mChoreographer;
- private final InputManager mInputManager;
private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
private final int mDisplayId;
private final IBinder mClientToken;
- private final InputTransferToken mInputTransferToken;
private final SurfaceControl mDecorationSurface;
private final InputChannel mInputChannel;
private final TaskResizeInputEventReceiver mInputEventReceiver;
- private final DragPositioningCallback mCallback;
private final SurfaceControl mInputSinkSurface;
private final IBinder mSinkClientToken;
private final InputChannel mSinkInputChannel;
private final DisplayController mDisplayController;
-
- private int mTaskWidth;
- private int mTaskHeight;
- private int mResizeHandleThickness;
- private int mCornerSize;
- private int mTaskCornerRadius;
-
- private Rect mLeftTopCornerBounds;
- private Rect mRightTopCornerBounds;
- private Rect mLeftBottomCornerBounds;
- private Rect mRightBottomCornerBounds;
-
- private int mDragPointerId = -1;
- private DragDetector mDragDetector;
private final Region mTouchRegion = new Region();
DragResizeInputListener(
@@ -107,23 +90,17 @@
Handler handler,
Choreographer choreographer,
int displayId,
- int taskCornerRadius,
SurfaceControl decorationSurface,
DragPositioningCallback callback,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
DisplayController displayController) {
- mInputManager = context.getSystemService(InputManager.class);
- mContext = context;
- mHandler = handler;
- mChoreographer = choreographer;
mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
mDisplayId = displayId;
- mTaskCornerRadius = taskCornerRadius;
mDecorationSurface = decorationSurface;
mDisplayController = displayController;
mClientToken = new Binder();
- mInputTransferToken = new InputTransferToken();
+ final InputTransferToken inputTransferToken = new InputTransferToken();
mInputChannel = new InputChannel();
try {
mWindowSession.grantInputChannel(
@@ -136,18 +113,19 @@
INPUT_FEATURE_SPY,
TYPE_APPLICATION,
null /* windowToken */,
- mInputTransferToken,
+ inputTransferToken,
TAG + " of " + decorationSurface.toString(),
mInputChannel);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
- mInputEventReceiver = new TaskResizeInputEventReceiver(
- mInputChannel, mHandler, mChoreographer);
- mCallback = callback;
- mDragDetector = new DragDetector(mInputEventReceiver);
- mDragDetector.setTouchSlop(ViewConfiguration.get(context).getScaledTouchSlop());
+ mInputEventReceiver = new TaskResizeInputEventReceiver(context, mInputChannel, callback,
+ handler, choreographer, () -> {
+ final DisplayLayout layout = mDisplayController.getDisplayLayout(mDisplayId);
+ return new Size(layout.width(), layout.height());
+ }, this::updateSinkInputChannel);
+ mInputEventReceiver.setTouchSlop(ViewConfiguration.get(context).getScaledTouchSlop());
mInputSinkSurface = surfaceControlBuilderSupplier.get()
.setName("TaskInputSink of " + decorationSurface)
@@ -171,7 +149,7 @@
INPUT_FEATURE_NO_INPUT_CHANNEL,
TYPE_INPUT_CONSUMER,
null /* windowToken */,
- mInputTransferToken,
+ inputTransferToken,
"TaskInputSink of " + decorationSurface,
mSinkInputChannel);
} catch (RemoteException e) {
@@ -182,86 +160,26 @@
/**
* Updates the geometry (the touch region) of this drag resize handler.
*
- * @param taskWidth The width of the task.
- * @param taskHeight The height of the task.
- * @param resizeHandleThickness The thickness of the resize handle in pixels.
- * @param cornerSize The size of the resize handle centered in each corner.
- * @param touchSlop The distance in pixels user has to drag with touch for it to register as
- * a resize action.
+ * @param incomingGeometry The geometry update to apply for this task's drag resize regions.
+ * @param touchSlop The distance in pixels user has to drag with touch for it to register
+ * as a resize action.
* @return whether the geometry has changed or not
*/
- boolean setGeometry(int taskWidth, int taskHeight, int resizeHandleThickness, int cornerSize,
- int touchSlop) {
- if (mTaskWidth == taskWidth && mTaskHeight == taskHeight
- && mResizeHandleThickness == resizeHandleThickness
- && mCornerSize == cornerSize) {
+ boolean setGeometry(@NonNull DragResizeWindowGeometry incomingGeometry, int touchSlop) {
+ DragResizeWindowGeometry geometry = mInputEventReceiver.getGeometry();
+ if (incomingGeometry.equals(geometry)) {
+ // Geometry hasn't changed size so skip all updates.
return false;
+ } else {
+ geometry = incomingGeometry;
}
-
- mTaskWidth = taskWidth;
- mTaskHeight = taskHeight;
- mResizeHandleThickness = resizeHandleThickness;
- mCornerSize = cornerSize;
- mDragDetector.setTouchSlop(touchSlop);
+ mInputEventReceiver.setTouchSlop(touchSlop);
mTouchRegion.setEmpty();
- final Rect topInputBounds = new Rect(
- -mResizeHandleThickness,
- -mResizeHandleThickness,
- mTaskWidth + mResizeHandleThickness,
- 0);
- mTouchRegion.union(topInputBounds);
-
- final Rect leftInputBounds = new Rect(
- -mResizeHandleThickness,
- 0,
- 0,
- mTaskHeight);
- mTouchRegion.union(leftInputBounds);
-
- final Rect rightInputBounds = new Rect(
- mTaskWidth,
- 0,
- mTaskWidth + mResizeHandleThickness,
- mTaskHeight);
- mTouchRegion.union(rightInputBounds);
-
- final Rect bottomInputBounds = new Rect(
- -mResizeHandleThickness,
- mTaskHeight,
- mTaskWidth + mResizeHandleThickness,
- mTaskHeight + mResizeHandleThickness);
- mTouchRegion.union(bottomInputBounds);
-
- // Set up touch areas in each corner.
- int cornerRadius = mCornerSize / 2;
- mLeftTopCornerBounds = new Rect(
- -cornerRadius,
- -cornerRadius,
- cornerRadius,
- cornerRadius);
- mTouchRegion.union(mLeftTopCornerBounds);
-
- mRightTopCornerBounds = new Rect(
- mTaskWidth - cornerRadius,
- -cornerRadius,
- mTaskWidth + cornerRadius,
- cornerRadius);
- mTouchRegion.union(mRightTopCornerBounds);
-
- mLeftBottomCornerBounds = new Rect(
- -cornerRadius,
- mTaskHeight - cornerRadius,
- cornerRadius,
- mTaskHeight + cornerRadius);
- mTouchRegion.union(mLeftBottomCornerBounds);
-
- mRightBottomCornerBounds = new Rect(
- mTaskWidth - cornerRadius,
- mTaskHeight - cornerRadius,
- mTaskWidth + cornerRadius,
- mTaskHeight + cornerRadius);
- mTouchRegion.union(mRightBottomCornerBounds);
+ // Apply the geometry to the touch region.
+ geometry.union(mTouchRegion);
+ mInputEventReceiver.setGeometry(geometry);
+ mInputEventReceiver.setTouchRegion(mTouchRegion);
try {
mWindowSession.updateInputChannel(
@@ -276,8 +194,9 @@
e.rethrowFromSystemServer();
}
+ final Size taskSize = geometry.getTaskSize();
mSurfaceControlTransactionSupplier.get()
- .setWindowCrop(mInputSinkSurface, mTaskWidth, mTaskHeight)
+ .setWindowCrop(mInputSinkSurface, taskSize.getWidth(), taskSize.getHeight())
.apply();
// The touch region of the TaskInputSink should be the touch region of this
// DragResizeInputHandler minus the task bounds. Pilfering events isn't enough to prevent
@@ -290,21 +209,16 @@
// issue. However, were there touchscreen-only a region out of the task bounds, mouse
// gestures will become no-op in that region, even though the mouse gestures may appear to
// be performed on the input window behind the resize handle.
- mTouchRegion.op(0, 0, mTaskWidth, mTaskHeight, Region.Op.DIFFERENCE);
+ mTouchRegion.op(0, 0, taskSize.getWidth(), taskSize.getHeight(), Region.Op.DIFFERENCE);
updateSinkInputChannel(mTouchRegion);
return true;
}
/**
- * Generate a Region that encapsulates all 4 corner handles
+ * Generate a Region that encapsulates all 4 corner handles and window edges.
*/
- Region getCornersRegion() {
- Region region = new Region();
- region.union(mLeftTopCornerBounds);
- region.union(mLeftBottomCornerBounds);
- region.union(mRightTopCornerBounds);
- region.union(mRightBottomCornerBounds);
- return region;
+ @NonNull Region getCornersRegion() {
+ return mInputEventReceiver.getCornersRegion();
}
private void updateSinkInputChannel(Region region) {
@@ -322,7 +236,7 @@
}
}
- boolean shouldHandleEvent(MotionEvent e, Point offset) {
+ boolean shouldHandleEvent(@NonNull MotionEvent e, @NonNull Point offset) {
return mInputEventReceiver.shouldHandleEvent(e, offset);
}
@@ -351,19 +265,37 @@
.apply();
}
- private class TaskResizeInputEventReceiver extends InputEventReceiver
- implements DragDetector.MotionEventHandler {
- private final Choreographer mChoreographer;
- private final Runnable mConsumeBatchEventRunnable;
+ private static class TaskResizeInputEventReceiver extends InputEventReceiver implements
+ DragDetector.MotionEventHandler {
+ @NonNull private final Context mContext;
+ private final InputManager mInputManager;
+ @NonNull private final InputChannel mInputChannel;
+ @NonNull private final DragPositioningCallback mCallback;
+ @NonNull private final Choreographer mChoreographer;
+ @NonNull private final Runnable mConsumeBatchEventRunnable;
+ @NonNull private final DragDetector mDragDetector;
+ @NonNull private final Supplier<Size> mDisplayLayoutSizeSupplier;
+ @NonNull private final Consumer<Region> mTouchRegionConsumer;
+ private final Rect mTmpRect = new Rect();
private boolean mConsumeBatchEventScheduled;
+ private DragResizeWindowGeometry mDragResizeWindowGeometry;
+ private Region mTouchRegion;
private boolean mShouldHandleEvents;
private int mLastCursorType = PointerIcon.TYPE_DEFAULT;
private Rect mDragStartTaskBounds;
- private final Rect mTmpRect = new Rect();
+ private int mDragPointerId = -1;
- private TaskResizeInputEventReceiver(
- InputChannel inputChannel, Handler handler, Choreographer choreographer) {
+ private TaskResizeInputEventReceiver(@NonNull Context context,
+ @NonNull InputChannel inputChannel,
+ @NonNull DragPositioningCallback callback, @NonNull Handler handler,
+ @NonNull Choreographer choreographer,
+ @NonNull Supplier<Size> displayLayoutSizeSupplier,
+ @NonNull Consumer<Region> touchRegionConsumer) {
super(inputChannel, handler.getLooper());
+ mContext = context;
+ mInputManager = context.getSystemService(InputManager.class);
+ mInputChannel = inputChannel;
+ mCallback = callback;
mChoreographer = choreographer;
mConsumeBatchEventRunnable = () -> {
@@ -376,6 +308,48 @@
scheduleConsumeBatchEvent();
}
};
+
+ mDragDetector = new DragDetector(this);
+ mDisplayLayoutSizeSupplier = displayLayoutSizeSupplier;
+ mTouchRegionConsumer = touchRegionConsumer;
+ }
+
+ /**
+ * Returns the geometry of the areas to drag resize.
+ */
+ DragResizeWindowGeometry getGeometry() {
+ return mDragResizeWindowGeometry;
+ }
+
+ /**
+ * Updates the geometry of the areas to drag resize.
+ */
+ void setGeometry(@NonNull DragResizeWindowGeometry dragResizeWindowGeometry) {
+ mDragResizeWindowGeometry = dragResizeWindowGeometry;
+ }
+
+ /**
+ * Sets how much slop to allow for touches.
+ */
+ void setTouchSlop(int touchSlop) {
+ mDragDetector.setTouchSlop(touchSlop);
+ }
+
+ /**
+ * Updates the region accepting input for drag resizing the task.
+ */
+ void setTouchRegion(@NonNull Region touchRegion) {
+ mTouchRegion = touchRegion;
+ }
+
+ /**
+ * Returns the union of all regions that can be touched for drag resizing; the corners and
+ * window edges.
+ */
+ @NonNull Region getCornersRegion() {
+ Region region = new Region();
+ mDragResizeWindowGeometry.union(region);
+ return region;
}
@Override
@@ -416,14 +390,15 @@
boolean isTouch = isTouchEvent(e);
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
- mShouldHandleEvents = shouldHandleEvent(e, isTouch, new Point() /* offset */);
+ mShouldHandleEvents = mDragResizeWindowGeometry.shouldHandleEvent(e, isTouch,
+ new Point() /* offset */);
if (mShouldHandleEvents) {
mDragPointerId = e.getPointerId(0);
float x = e.getX(0);
float y = e.getY(0);
float rawX = e.getRawX(0);
float rawY = e.getRawY(0);
- int ctrlType = calculateCtrlType(isTouch, x, y);
+ int ctrlType = mDragResizeWindowGeometry.calculateCtrlType(isTouch, x, y);
mDragStartTaskBounds = mCallback.onDragPositioningStart(ctrlType,
rawX, rawY);
// Increase the input sink region to cover the whole screen; this is to
@@ -455,7 +430,7 @@
// If taskBounds has changed, setGeometry will be called and update the
// sink region. Otherwise, we should revert it here.
if (taskBounds.equals(mDragStartTaskBounds)) {
- updateSinkInputChannel(mTouchRegion);
+ mTouchRegionConsumer.accept(mTouchRegion);
}
}
mShouldHandleEvents = false;
@@ -480,125 +455,20 @@
private void updateInputSinkRegionForDrag(Rect taskBounds) {
mTmpRect.set(taskBounds);
- final DisplayLayout layout = mDisplayController.getDisplayLayout(mDisplayId);
- final Region dragTouchRegion = new Region(-taskBounds.left,
- -taskBounds.top,
- -taskBounds.left + layout.width(),
- -taskBounds.top + layout.height());
+ final Size displayLayoutSize = mDisplayLayoutSizeSupplier.get();
+ final Region dragTouchRegion = new Region(-taskBounds.left, -taskBounds.top,
+ -taskBounds.left + displayLayoutSize.getWidth(),
+ -taskBounds.top + displayLayoutSize.getHeight());
// Remove the localized task bounds from the touch region.
mTmpRect.offsetTo(0, 0);
dragTouchRegion.op(mTmpRect, Region.Op.DIFFERENCE);
- updateSinkInputChannel(dragTouchRegion);
- }
-
- private boolean isInCornerBounds(float xf, float yf) {
- return calculateCornersCtrlType(xf, yf) != 0;
- }
-
- private boolean isInResizeHandleBounds(float x, float y) {
- return calculateResizeHandlesCtrlType(x, y) != 0;
- }
-
- @DragPositioningCallback.CtrlType
- private int calculateCtrlType(boolean isTouch, float x, float y) {
- if (isTouch) {
- return calculateCornersCtrlType(x, y);
- }
- return calculateResizeHandlesCtrlType(x, y);
- }
-
- @DragPositioningCallback.CtrlType
- private int calculateResizeHandlesCtrlType(float x, float y) {
- int ctrlType = 0;
- // mTaskCornerRadius is only used in comparing with corner regions. Comparisons with
- // sides will use the bounds specified in setGeometry and not go into task bounds.
- if (x < mTaskCornerRadius) {
- ctrlType |= CTRL_TYPE_LEFT;
- }
- if (x > mTaskWidth - mTaskCornerRadius) {
- ctrlType |= CTRL_TYPE_RIGHT;
- }
- if (y < mTaskCornerRadius) {
- ctrlType |= CTRL_TYPE_TOP;
- }
- if (y > mTaskHeight - mTaskCornerRadius) {
- ctrlType |= CTRL_TYPE_BOTTOM;
- }
- // Check distances from the center if it's in one of four corners.
- if ((ctrlType & (CTRL_TYPE_LEFT | CTRL_TYPE_RIGHT)) != 0
- && (ctrlType & (CTRL_TYPE_TOP | CTRL_TYPE_BOTTOM)) != 0) {
- return checkDistanceFromCenter(ctrlType, x, y);
- }
- // Otherwise, we should make sure we don't resize tasks inside task bounds.
- return (x < 0 || y < 0 || x >= mTaskWidth || y >= mTaskHeight) ? ctrlType : 0;
- }
-
- // If corner input is not within appropriate distance of corner radius, do not use it.
- // If input is not on a corner or is within valid distance, return ctrlType.
- @DragPositioningCallback.CtrlType
- private int checkDistanceFromCenter(@DragPositioningCallback.CtrlType int ctrlType,
- float x, float y) {
- int centerX;
- int centerY;
-
- // Determine center of rounded corner circle; this is simply the corner if radius is 0.
- switch (ctrlType) {
- case CTRL_TYPE_LEFT | CTRL_TYPE_TOP: {
- centerX = mTaskCornerRadius;
- centerY = mTaskCornerRadius;
- break;
- }
- case CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM: {
- centerX = mTaskCornerRadius;
- centerY = mTaskHeight - mTaskCornerRadius;
- break;
- }
- case CTRL_TYPE_RIGHT | CTRL_TYPE_TOP: {
- centerX = mTaskWidth - mTaskCornerRadius;
- centerY = mTaskCornerRadius;
- break;
- }
- case CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM: {
- centerX = mTaskWidth - mTaskCornerRadius;
- centerY = mTaskHeight - mTaskCornerRadius;
- break;
- }
- default: {
- throw new IllegalArgumentException("ctrlType should be complex, but it's 0x"
- + Integer.toHexString(ctrlType));
- }
- }
- double distanceFromCenter = Math.hypot(x - centerX, y - centerY);
-
- if (distanceFromCenter < mTaskCornerRadius + mResizeHandleThickness
- && distanceFromCenter >= mTaskCornerRadius) {
- return ctrlType;
- }
- return 0;
- }
-
- @DragPositioningCallback.CtrlType
- private int calculateCornersCtrlType(float x, float y) {
- int xi = (int) x;
- int yi = (int) y;
- if (mLeftTopCornerBounds.contains(xi, yi)) {
- return CTRL_TYPE_LEFT | CTRL_TYPE_TOP;
- }
- if (mLeftBottomCornerBounds.contains(xi, yi)) {
- return CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM;
- }
- if (mRightTopCornerBounds.contains(xi, yi)) {
- return CTRL_TYPE_RIGHT | CTRL_TYPE_TOP;
- }
- if (mRightBottomCornerBounds.contains(xi, yi)) {
- return CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM;
- }
- return 0;
+ mTouchRegionConsumer.accept(dragTouchRegion);
}
private void updateCursorType(int displayId, int deviceId, int pointerId, float x,
float y) {
- @DragPositioningCallback.CtrlType int ctrlType = calculateResizeHandlesCtrlType(x, y);
+ @DragPositioningCallback.CtrlType int ctrlType =
+ mDragResizeWindowGeometry.calculateCtrlType(/* isTouch= */ false, x, y);
int cursorType = PointerIcon.TYPE_DEFAULT;
switch (ctrlType) {
@@ -640,19 +510,7 @@
}
private boolean shouldHandleEvent(MotionEvent e, Point offset) {
- return shouldHandleEvent(e, isTouchEvent(e), offset);
- }
-
- private boolean shouldHandleEvent(MotionEvent e, boolean isTouch, Point offset) {
- boolean result;
- final float x = e.getX(0) + offset.x;
- final float y = e.getY(0) + offset.y;
- if (isTouch) {
- result = isInCornerBounds(x, y);
- } else {
- result = isInResizeHandleBounds(x, y);
- }
- return result;
+ return mDragResizeWindowGeometry.shouldHandleEvent(e, offset);
}
private boolean isTouchEvent(MotionEvent e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
new file mode 100644
index 0000000..eafb569
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor;
+
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+
+import static com.android.window.flags.Flags.enableWindowingEdgeDragResize;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED;
+
+import android.annotation.NonNull;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.Size;
+import android.view.MotionEvent;
+
+import com.android.wm.shell.R;
+
+import java.util.Objects;
+
+/**
+ * Geometry for a drag resize region for a particular window.
+ */
+final class DragResizeWindowGeometry {
+ private final int mTaskCornerRadius;
+ private final Size mTaskSize;
+ // The size of the handle applied to the edges of the window, for the user to drag resize.
+ private final int mResizeHandleThickness;
+ // The task corners to permit drag resizing with a course input, such as touch.
+
+ private final @NonNull TaskCorners mLargeTaskCorners;
+ // The task corners to permit drag resizing with a fine input, such as stylus or cursor.
+ private final @NonNull TaskCorners mFineTaskCorners;
+ // The bounds for each edge drag region, which can resize the task in one direction.
+ private final @NonNull Rect mTopEdgeBounds;
+ private final @NonNull Rect mLeftEdgeBounds;
+ private final @NonNull Rect mRightEdgeBounds;
+ private final @NonNull Rect mBottomEdgeBounds;
+
+ DragResizeWindowGeometry(int taskCornerRadius, @NonNull Size taskSize,
+ int resizeHandleThickness, int fineCornerSize, int largeCornerSize) {
+ mTaskCornerRadius = taskCornerRadius;
+ mTaskSize = taskSize;
+ mResizeHandleThickness = resizeHandleThickness;
+
+ mLargeTaskCorners = new TaskCorners(mTaskSize, largeCornerSize);
+ mFineTaskCorners = new TaskCorners(mTaskSize, fineCornerSize);
+
+ // Save touch areas for each edge.
+ mTopEdgeBounds = new Rect(
+ -mResizeHandleThickness,
+ -mResizeHandleThickness,
+ mTaskSize.getWidth() + mResizeHandleThickness,
+ 0);
+ mLeftEdgeBounds = new Rect(
+ -mResizeHandleThickness,
+ 0,
+ 0,
+ mTaskSize.getHeight());
+ mRightEdgeBounds = new Rect(
+ mTaskSize.getWidth(),
+ 0,
+ mTaskSize.getWidth() + mResizeHandleThickness,
+ mTaskSize.getHeight());
+ mBottomEdgeBounds = new Rect(
+ -mResizeHandleThickness,
+ mTaskSize.getHeight(),
+ mTaskSize.getWidth() + mResizeHandleThickness,
+ mTaskSize.getHeight() + mResizeHandleThickness);
+ }
+
+ /**
+ * Returns the resource value to use for the resize handle on the edge of the window.
+ */
+ static int getResizeEdgeHandleSize(@NonNull Resources res) {
+ return enableWindowingEdgeDragResize()
+ ? res.getDimensionPixelSize(R.dimen.desktop_mode_edge_handle)
+ : res.getDimensionPixelSize(R.dimen.freeform_resize_handle);
+ }
+
+ /**
+ * Returns the resource value to use for course input, such as touch, that benefits from a large
+ * square on each of the window's corners.
+ */
+ static int getLargeResizeCornerSize(@NonNull Resources res) {
+ return res.getDimensionPixelSize(R.dimen.desktop_mode_corner_resize_large);
+ }
+
+ /**
+ * Returns the resource value to use for fine input, such as stylus, that can use a smaller
+ * square on each of the window's corners.
+ */
+ static int getFineResizeCornerSize(@NonNull Resources res) {
+ return res.getDimensionPixelSize(R.dimen.freeform_resize_corner);
+ }
+
+ /**
+ * Returns the size of the task this geometry is calculated for.
+ */
+ @NonNull Size getTaskSize() {
+ // Safe to return directly since size is immutable.
+ return mTaskSize;
+ }
+
+ /**
+ * Returns the union of all regions that can be touched for drag resizing; the corners window
+ * and window edges.
+ */
+ void union(@NonNull Region region) {
+ // Apply the edge resize regions.
+ region.union(mTopEdgeBounds);
+ region.union(mLeftEdgeBounds);
+ region.union(mRightEdgeBounds);
+ region.union(mBottomEdgeBounds);
+
+ if (enableWindowingEdgeDragResize()) {
+ // Apply the corners as well for the larger corners, to ensure we capture all possible
+ // touches.
+ mLargeTaskCorners.union(region);
+ } else {
+ // Only apply fine corners for the legacy approach.
+ mFineTaskCorners.union(region);
+ }
+ }
+
+ /**
+ * Returns if this MotionEvent should be handled, based on its source and position.
+ */
+ boolean shouldHandleEvent(@NonNull MotionEvent e, @NonNull Point offset) {
+ return shouldHandleEvent(e, isTouchEvent(e), offset);
+ }
+
+ /**
+ * Returns if this MotionEvent should be handled, based on its source and position.
+ */
+ boolean shouldHandleEvent(@NonNull MotionEvent e, boolean isTouch, @NonNull Point offset) {
+ final float x = e.getX(0) + offset.x;
+ final float y = e.getY(0) + offset.y;
+
+ if (enableWindowingEdgeDragResize()) {
+ // First check if touch falls within a corner.
+ // Large corner bounds are used for course input like touch, otherwise fine bounds.
+ boolean result = isTouch
+ ? isInCornerBounds(mLargeTaskCorners, x, y)
+ : isInCornerBounds(mFineTaskCorners, x, y);
+ // Check if touch falls within the edge resize handle, since edge resizing can apply
+ // for any input source.
+ if (!result) {
+ result = isInEdgeResizeBounds(x, y);
+ }
+ return result;
+ } else {
+ // Legacy uses only fine corners for touch, and edges only for non-touch input.
+ return isTouch
+ ? isInCornerBounds(mFineTaskCorners, x, y)
+ : isInEdgeResizeBounds(x, y);
+ }
+ }
+
+ private boolean isTouchEvent(@NonNull MotionEvent e) {
+ return (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
+ }
+
+ private boolean isInCornerBounds(TaskCorners corners, float xf, float yf) {
+ return corners.calculateCornersCtrlType(xf, yf) != 0;
+ }
+
+ private boolean isInEdgeResizeBounds(float x, float y) {
+ return calculateEdgeResizeCtrlType(x, y) != 0;
+ }
+
+ /**
+ * Returns the control type for the drag-resize, based on the touch regions and this
+ * MotionEvent's coordinates.
+ */
+ @DragPositioningCallback.CtrlType
+ int calculateCtrlType(boolean isTouch, float x, float y) {
+ if (enableWindowingEdgeDragResize()) {
+ // First check if touch falls within a corner.
+ // Large corner bounds are used for course input like touch, otherwise fine bounds.
+ int ctrlType = isTouch
+ ? mLargeTaskCorners.calculateCornersCtrlType(x, y)
+ : mFineTaskCorners.calculateCornersCtrlType(x, y);
+ // Check if touch falls within the edge resize handle, since edge resizing can apply
+ // for any input source.
+ if (ctrlType == CTRL_TYPE_UNDEFINED) {
+ ctrlType = calculateEdgeResizeCtrlType(x, y);
+ }
+ return ctrlType;
+ } else {
+ // Legacy uses only fine corners for touch, and edges only for non-touch input.
+ return isTouch
+ ? mFineTaskCorners.calculateCornersCtrlType(x, y)
+ : calculateEdgeResizeCtrlType(x, y);
+ }
+ }
+
+ @DragPositioningCallback.CtrlType
+ private int calculateEdgeResizeCtrlType(float x, float y) {
+ int ctrlType = CTRL_TYPE_UNDEFINED;
+ // mTaskCornerRadius is only used in comparing with corner regions. Comparisons with
+ // sides will use the bounds specified in setGeometry and not go into task bounds.
+ if (x < mTaskCornerRadius) {
+ ctrlType |= CTRL_TYPE_LEFT;
+ }
+ if (x > mTaskSize.getWidth() - mTaskCornerRadius) {
+ ctrlType |= CTRL_TYPE_RIGHT;
+ }
+ if (y < mTaskCornerRadius) {
+ ctrlType |= CTRL_TYPE_TOP;
+ }
+ if (y > mTaskSize.getHeight() - mTaskCornerRadius) {
+ ctrlType |= CTRL_TYPE_BOTTOM;
+ }
+ // If the touch is within one of the four corners, check if it is within the bounds of the
+ // // handle.
+ if ((ctrlType & (CTRL_TYPE_LEFT | CTRL_TYPE_RIGHT)) != 0
+ && (ctrlType & (CTRL_TYPE_TOP | CTRL_TYPE_BOTTOM)) != 0) {
+ return checkDistanceFromCenter(ctrlType, x, y);
+ }
+ // Otherwise, we should make sure we don't resize tasks inside task bounds.
+ return (x < 0 || y < 0 || x >= mTaskSize.getWidth() || y >= mTaskSize.getHeight())
+ ? ctrlType : CTRL_TYPE_UNDEFINED;
+ }
+
+ /**
+ * Return {@code ctrlType} if the corner input is outside the (potentially rounded) corner of
+ * the task, and within the thickness of the resize handle. Otherwise, return 0.
+ */
+ @DragPositioningCallback.CtrlType
+ private int checkDistanceFromCenter(@DragPositioningCallback.CtrlType int ctrlType, float x,
+ float y) {
+ final Point cornerRadiusCenter = calculateCenterForCornerRadius(ctrlType);
+ double distanceFromCenter = Math.hypot(x - cornerRadiusCenter.x, y - cornerRadiusCenter.y);
+
+ if (distanceFromCenter < mTaskCornerRadius + mResizeHandleThickness
+ && distanceFromCenter >= mTaskCornerRadius) {
+ return ctrlType;
+ }
+ return CTRL_TYPE_UNDEFINED;
+ }
+
+ /**
+ * Returns center of rounded corner circle; this is simply the corner if radius is 0.
+ */
+ private Point calculateCenterForCornerRadius(@DragPositioningCallback.CtrlType int ctrlType) {
+ int centerX;
+ int centerY;
+
+ switch (ctrlType) {
+ case CTRL_TYPE_LEFT | CTRL_TYPE_TOP: {
+ centerX = mTaskCornerRadius;
+ centerY = mTaskCornerRadius;
+ break;
+ }
+ case CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM: {
+ centerX = mTaskCornerRadius;
+ centerY = mTaskSize.getHeight() - mTaskCornerRadius;
+ break;
+ }
+ case CTRL_TYPE_RIGHT | CTRL_TYPE_TOP: {
+ centerX = mTaskSize.getWidth() - mTaskCornerRadius;
+ centerY = mTaskCornerRadius;
+ break;
+ }
+ case CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM: {
+ centerX = mTaskSize.getWidth() - mTaskCornerRadius;
+ centerY = mTaskSize.getHeight() - mTaskCornerRadius;
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException(
+ "ctrlType should be complex, but it's 0x" + Integer.toHexString(ctrlType));
+ }
+ }
+ return new Point(centerX, centerY);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (this == obj) return true;
+ if (!(obj instanceof DragResizeWindowGeometry other)) return false;
+
+ return this.mTaskCornerRadius == other.mTaskCornerRadius
+ && this.mTaskSize.equals(other.mTaskSize)
+ && this.mResizeHandleThickness == other.mResizeHandleThickness
+ && this.mFineTaskCorners.equals(other.mFineTaskCorners)
+ && this.mLargeTaskCorners.equals(other.mLargeTaskCorners)
+ && this.mTopEdgeBounds.equals(other.mTopEdgeBounds)
+ && this.mLeftEdgeBounds.equals(other.mLeftEdgeBounds)
+ && this.mRightEdgeBounds.equals(other.mRightEdgeBounds)
+ && this.mBottomEdgeBounds.equals(other.mBottomEdgeBounds);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mTaskCornerRadius,
+ mTaskSize,
+ mResizeHandleThickness,
+ mFineTaskCorners,
+ mLargeTaskCorners,
+ mTopEdgeBounds,
+ mLeftEdgeBounds,
+ mRightEdgeBounds,
+ mBottomEdgeBounds);
+ }
+
+ /**
+ * Representation of the drag resize regions at the corner of the window.
+ */
+ private static class TaskCorners {
+ // The size of the square applied to the corners of the window, for the user to drag
+ // resize.
+ private final int mCornerSize;
+ // The square for each corner.
+ private final @NonNull Rect mLeftTopCornerBounds;
+ private final @NonNull Rect mRightTopCornerBounds;
+ private final @NonNull Rect mLeftBottomCornerBounds;
+ private final @NonNull Rect mRightBottomCornerBounds;
+
+ TaskCorners(@NonNull Size taskSize, int cornerSize) {
+ mCornerSize = cornerSize;
+ final int cornerRadius = cornerSize / 2;
+ mLeftTopCornerBounds = new Rect(
+ -cornerRadius,
+ -cornerRadius,
+ cornerRadius,
+ cornerRadius);
+
+ mRightTopCornerBounds = new Rect(
+ taskSize.getWidth() - cornerRadius,
+ -cornerRadius,
+ taskSize.getWidth() + cornerRadius,
+ cornerRadius);
+
+ mLeftBottomCornerBounds = new Rect(
+ -cornerRadius,
+ taskSize.getHeight() - cornerRadius,
+ cornerRadius,
+ taskSize.getHeight() + cornerRadius);
+
+ mRightBottomCornerBounds = new Rect(
+ taskSize.getWidth() - cornerRadius,
+ taskSize.getHeight() - cornerRadius,
+ taskSize.getWidth() + cornerRadius,
+ taskSize.getHeight() + cornerRadius);
+ }
+
+ /**
+ * Updates the region to include all four corners.
+ */
+ void union(Region region) {
+ region.union(mLeftTopCornerBounds);
+ region.union(mRightTopCornerBounds);
+ region.union(mLeftBottomCornerBounds);
+ region.union(mRightBottomCornerBounds);
+ }
+
+ /**
+ * Returns the control type based on the position of the {@code MotionEvent}'s coordinates.
+ */
+ @DragPositioningCallback.CtrlType
+ int calculateCornersCtrlType(float x, float y) {
+ int xi = (int) x;
+ int yi = (int) y;
+ if (mLeftTopCornerBounds.contains(xi, yi)) {
+ return CTRL_TYPE_LEFT | CTRL_TYPE_TOP;
+ }
+ if (mLeftBottomCornerBounds.contains(xi, yi)) {
+ return CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM;
+ }
+ if (mRightTopCornerBounds.contains(xi, yi)) {
+ return CTRL_TYPE_RIGHT | CTRL_TYPE_TOP;
+ }
+ if (mRightBottomCornerBounds.contains(xi, yi)) {
+ return CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM;
+ }
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "TaskCorners of size " + mCornerSize + " for the"
+ + " top left " + mLeftTopCornerBounds
+ + " top right " + mRightTopCornerBounds
+ + " bottom left " + mLeftBottomCornerBounds
+ + " bottom right " + mRightBottomCornerBounds;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (this == obj) return true;
+ if (!(obj instanceof TaskCorners other)) return false;
+
+ return this.mCornerSize == other.mCornerSize
+ && this.mLeftTopCornerBounds.equals(other.mLeftTopCornerBounds)
+ && this.mRightTopCornerBounds.equals(other.mRightTopCornerBounds)
+ && this.mLeftBottomCornerBounds.equals(other.mLeftBottomCornerBounds)
+ && this.mRightBottomCornerBounds.equals(other.mRightBottomCornerBounds);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mCornerSize,
+ mLeftTopCornerBounds,
+ mRightTopCornerBounds,
+ mLeftBottomCornerBounds,
+ mRightBottomCornerBounds);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleImageButton.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleImageButton.kt
new file mode 100644
index 0000000..b21c3f5
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleImageButton.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.ImageButton
+
+/**
+ * [ImageButton] for the handle at the top of fullscreen apps. Has custom hover
+ * and press handling to grow the handle on hover enter and shrink the handle on
+ * hover exit and press.
+ */
+class HandleImageButton (context: Context?, attrs: AttributeSet?) :
+ ImageButton(context, attrs) {
+ private val handleAnimator = ValueAnimator()
+
+ override fun onHoverChanged(hovered: Boolean) {
+ super.onHoverChanged(hovered)
+ if (hovered) {
+ animateHandle(HANDLE_HOVER_ANIM_DURATION, HANDLE_HOVER_ENTER_SCALE)
+ } else {
+ if (!isPressed) {
+ animateHandle(HANDLE_HOVER_ANIM_DURATION, HANDLE_DEFAULT_SCALE)
+ }
+ }
+ }
+
+ override fun setPressed(pressed: Boolean) {
+ if (isPressed != pressed) {
+ super.setPressed(pressed)
+ if (pressed) {
+ animateHandle(HANDLE_PRESS_ANIM_DURATION, HANDLE_PRESS_DOWN_SCALE)
+ } else {
+ animateHandle(HANDLE_PRESS_ANIM_DURATION, HANDLE_DEFAULT_SCALE)
+ }
+ }
+ }
+
+ private fun animateHandle(duration: Long, endScale: Float) {
+ if (handleAnimator.isRunning) {
+ handleAnimator.cancel()
+ }
+ handleAnimator.duration = duration
+ handleAnimator.setFloatValues(scaleX, endScale)
+ handleAnimator.addUpdateListener { animator ->
+ scaleX = animator.animatedValue as Float
+ }
+ handleAnimator.start()
+ }
+
+ companion object {
+ /** The duration of animations related to hover state. **/
+ private const val HANDLE_HOVER_ANIM_DURATION = 300L
+ /** The duration of animations related to pressed state. **/
+ private const val HANDLE_PRESS_ANIM_DURATION = 200L
+ /** Ending scale for hover enter. **/
+ private const val HANDLE_HOVER_ENTER_SCALE = 1.2f
+ /** Ending scale for press down. **/
+ private const val HANDLE_PRESS_DOWN_SCALE = 0.85f
+ /** Default scale for handle. **/
+ private const val HANDLE_DEFAULT_SCALE = 1f
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt
index 2fda3ea..7898567 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt
@@ -21,7 +21,7 @@
import android.widget.ImageButton
/**
- * A custom [ImageButton] that intentionally does not handle hover events.
+ * A custom [ImageButton] for buttons inside handle menu that intentionally doesn't handle hovers.
* This is due to the hover events being handled by [DesktopModeWindowDecorViewModel]
* in order to take the status bar layer into account. Handling it in both classes results in a
* flicker when the hover moves from outside to inside status bar layer.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
index 987aadf..74499c7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
@@ -33,6 +33,7 @@
get() = dragToDesktopAnimator.animatedValue as Float * startBounds.width()
val scale: Float
get() = dragToDesktopAnimator.animatedValue as Float
+ private val mostRecentInput = PointF()
private val dragToDesktopAnimator: ValueAnimator = ValueAnimator.ofFloat(1f,
DRAG_FREEFORM_SCALE)
.setDuration(ANIMATION_DURATION.toLong())
@@ -40,9 +41,13 @@
val t = SurfaceControl.Transaction()
val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
addUpdateListener {
+ setTaskPosition(mostRecentInput.x, mostRecentInput.y)
t.setScale(taskSurface, scale, scale)
- .setCornerRadius(taskSurface, cornerRadius)
- .apply()
+ .setCornerRadius(taskSurface, cornerRadius)
+ .setScale(taskSurface, scale, scale)
+ .setCornerRadius(taskSurface, cornerRadius)
+ .setPosition(taskSurface, position.x, position.y)
+ .apply()
}
}
@@ -78,19 +83,28 @@
// allow dragging beyond its stage across any region of the display. Because of that, the
// rawX/Y are more true to where the gesture is on screen and where the surface should be
// positioned.
- position.x = ev.rawX - animatedTaskWidth / 2
- position.y = ev.rawY
+ mostRecentInput.set(ev.rawX, ev.rawY)
- if (!allowSurfaceChangesOnMove) {
+ // If animator is running, allow it to set scale and position at the same time.
+ if (!allowSurfaceChangesOnMove || dragToDesktopAnimator.isRunning) {
return
}
-
+ setTaskPosition(ev.rawX, ev.rawY)
val t = transactionFactory()
t.setPosition(taskSurface, position.x, position.y)
t.apply()
}
/**
+ * Calculates the top left corner of task from input coordinates.
+ * Top left will be needed for the resulting surface control transaction.
+ */
+ private fun setTaskPosition(x: Float, y: Float) {
+ position.x = x - animatedTaskWidth / 2
+ position.y = y
+ }
+
+ /**
* Cancels the animation, intended to be used when another animator will take over.
*/
fun cancelAnimator() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
index d0fcd86..53d4e27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
@@ -72,7 +72,10 @@
}
void closeTask(WindowContainerToken taskToken) {
- WindowContainerTransaction wct = new WindowContainerTransaction();
+ closeTask(taskToken, new WindowContainerTransaction());
+ }
+
+ void closeTask(WindowContainerToken taskToken, WindowContainerTransaction wct) {
wct.removeTask(taskToken);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mTransitionStarter.startRemoveTransition(wct);
@@ -91,14 +94,12 @@
}
}
- void maximizeTask(RunningTaskInfo taskInfo) {
+ void maximizeTask(RunningTaskInfo taskInfo, int containerWindowingMode) {
WindowContainerTransaction wct = new WindowContainerTransaction();
int targetWindowingMode = taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_FREEFORM;
- int displayWindowingMode =
- taskInfo.configuration.windowConfiguration.getDisplayWindowingMode();
wct.setWindowingMode(taskInfo.token,
- targetWindowingMode == displayWindowingMode
+ targetWindowingMode == containerWindowingMode
? WINDOWING_MODE_UNDEFINED : targetWindowingMode);
if (targetWindowingMode == WINDOWING_MODE_FULLSCREEN) {
wct.setBounds(taskInfo.token, null);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 32c2d1e..51b0a24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -40,7 +40,6 @@
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
-import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
@@ -293,60 +292,56 @@
.setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
.show(mCaptionContainerSurface);
- if (ViewRootImpl.CAPTION_ON_SHELL) {
- outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
+ outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
- // Caption insets
- if (mIsCaptionVisible) {
- // Caption inset is the full width of the task with the |captionHeight| and
- // positioned at the top of the task bounds, also in absolute coordinates.
- // So just reuse the task bounds and adjust the bottom coordinate.
- mCaptionInsetsRect.set(taskBounds);
- mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + outResult.mCaptionHeight;
+ // Caption insets
+ if (mIsCaptionVisible) {
+ // Caption inset is the full width of the task with the |captionHeight| and
+ // positioned at the top of the task bounds, also in absolute coordinates.
+ // So just reuse the task bounds and adjust the bottom coordinate.
+ mCaptionInsetsRect.set(taskBounds);
+ mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + outResult.mCaptionHeight;
- // Caption bounding rectangles: these are optional, and are used to present finer
- // insets than traditional |Insets| to apps about where their content is occluded.
- // These are also in absolute coordinates.
- final Rect[] boundingRects;
- final int numOfElements = params.mOccludingCaptionElements.size();
- if (numOfElements == 0) {
- boundingRects = null;
- } else {
- // The customizable region can at most be equal to the caption bar.
+ // Caption bounding rectangles: these are optional, and are used to present finer
+ // insets than traditional |Insets| to apps about where their content is occluded.
+ // These are also in absolute coordinates.
+ final Rect[] boundingRects;
+ final int numOfElements = params.mOccludingCaptionElements.size();
+ if (numOfElements == 0) {
+ boundingRects = null;
+ } else {
+ // The customizable region can at most be equal to the caption bar.
+ if (params.mAllowCaptionInputFallthrough) {
+ outResult.mCustomizableCaptionRegion.set(mCaptionInsetsRect);
+ }
+ boundingRects = new Rect[numOfElements];
+ for (int i = 0; i < numOfElements; i++) {
+ final OccludingCaptionElement element =
+ params.mOccludingCaptionElements.get(i);
+ final int elementWidthPx =
+ resources.getDimensionPixelSize(element.mWidthResId);
+ boundingRects[i] =
+ calculateBoundingRect(element, elementWidthPx, mCaptionInsetsRect);
+ // Subtract the regions used by the caption elements, the rest is
+ // customizable.
if (params.mAllowCaptionInputFallthrough) {
- outResult.mCustomizableCaptionRegion.set(mCaptionInsetsRect);
- }
- boundingRects = new Rect[numOfElements];
- for (int i = 0; i < numOfElements; i++) {
- final OccludingCaptionElement element =
- params.mOccludingCaptionElements.get(i);
- final int elementWidthPx =
- resources.getDimensionPixelSize(element.mWidthResId);
- boundingRects[i] =
- calculateBoundingRect(element, elementWidthPx, mCaptionInsetsRect);
- // Subtract the regions used by the caption elements, the rest is
- // customizable.
- if (params.mAllowCaptionInputFallthrough) {
- outResult.mCustomizableCaptionRegion.op(boundingRects[i],
- Region.Op.DIFFERENCE);
- }
+ outResult.mCustomizableCaptionRegion.op(boundingRects[i],
+ Region.Op.DIFFERENCE);
}
}
- // Add this caption as an inset source.
- wct.addInsetsSource(mTaskInfo.token,
- mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect,
- boundingRects);
- wct.addInsetsSource(mTaskInfo.token,
- mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
- mCaptionInsetsRect, null /* boundingRects */);
- } else {
- wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
- WindowInsets.Type.captionBar());
- wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
- WindowInsets.Type.mandatorySystemGestures());
}
+ // Add this caption as an inset source.
+ wct.addInsetsSource(mTaskInfo.token,
+ mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect,
+ boundingRects);
+ wct.addInsetsSource(mTaskInfo.token,
+ mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
+ mCaptionInsetsRect, null /* boundingRects */);
} else {
- startT.hide(mCaptionContainerSurface);
+ wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
+ WindowInsets.Type.captionBar());
+ wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
+ WindowInsets.Type.mandatorySystemGestures());
}
// Task surface itself
@@ -594,8 +589,7 @@
*/
public void addCaptionInset(WindowContainerTransaction wct) {
final int captionHeightId = getCaptionHeightId(mTaskInfo.getWindowingMode());
- if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL
- || !mIsCaptionVisible) {
+ if (captionHeightId == Resources.ID_NULL || !mIsCaptionVisible) {
return;
}
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
index 3380adac..e9eabb4 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
@@ -17,10 +17,10 @@
package com.android.wm.shell.flicker.appcompat
import android.content.Context
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.FlickerTestData
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.component.ComponentNameMatcher
import com.android.server.wm.flicker.helpers.LetterboxAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.BaseTest
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
index f08eba5..16c2d47 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
@@ -18,11 +18,11 @@
import android.platform.test.annotations.Postsubmit
import android.tools.flicker.assertions.FlickerTest
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
import androidx.test.filters.RequiresDevice
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
index 826fc54..d85b771 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
@@ -19,11 +19,11 @@
import android.platform.test.annotations.Postsubmit
import android.tools.Rotation
import android.tools.flicker.assertions.FlickerTest
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
import androidx.test.filters.RequiresDevice
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
index 26e78bf..164534c 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
@@ -16,17 +16,17 @@
package com.android.wm.shell.flicker.appcompat
+import android.graphics.Rect
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.RequiresDevice
import android.tools.NavBar
import android.tools.Rotation
-import android.tools.datatypes.Rect
import android.tools.flicker.assertions.FlickerTest
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -260,7 +260,7 @@
companion object {
/** {@inheritDoc} */
- private var startDisplayBounds = Rect.EMPTY
+ private var startDisplayBounds = Rect()
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
index 2aa84b4..034d54b 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
@@ -53,7 +53,7 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
class RepositionFixedPortraitAppTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) {
- val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation).bounds
+ val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
index 7ffa233..22543aa 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
@@ -16,19 +16,19 @@
package com.android.wm.shell.flicker.appcompat
+import android.graphics.Rect
import android.os.Build
import android.platform.test.annotations.Postsubmit
import android.system.helpers.CommandsHelper
import android.tools.NavBar
import android.tools.Rotation
-import android.tools.datatypes.Rect
import android.tools.flicker.assertions.FlickerTest
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.helpers.FIND_TIMEOUT
+import android.tools.traces.component.ComponentNameMatcher
import android.tools.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
@@ -167,7 +167,7 @@
}
companion object {
- private var startDisplayBounds = Rect.EMPTY
+ private var startDisplayBounds = Rect()
const val LAUNCHER_PACKAGE = "com.google.android.apps.nexuslauncher"
/**
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
index 521c0d0..2a9b107 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
@@ -19,11 +19,11 @@
import android.content.Context
import android.graphics.Point
import android.platform.test.annotations.Presubmit
-import android.tools.flicker.subject.layers.LayersTraceSubject
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.subject.layers.LayersTraceSubject
+import android.tools.traces.component.ComponentNameMatcher
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.test.uiautomator.By
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index e059ac7..9ef49c1 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
@@ -17,10 +17,10 @@
package com.android.wm.shell.flicker.bubble
import android.platform.test.annotations.Postsubmit
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.component.ComponentNameMatcher
import android.view.WindowInsets
import android.view.WindowManager
import androidx.test.filters.FlakyTest
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index d64bfed..b85d793 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -33,7 +33,7 @@
/**
* Test entering pip from an app via auto-enter property when navigating to home.
*
- * To run this test: `atest WMShellFlickerTests:AutoEnterPipOnGoToHomeTest`
+ * To run this test: `atest WMShellFlickerTestsPip1:AutoEnterPipOnGoToHomeTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
index a0edcfb..d059211 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
@@ -17,10 +17,10 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.component.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -30,7 +30,7 @@
/**
* Test auto entering pip using a source rect hint.
*
- * To run this test: `atest AutoEnterPipWithSourceRectHintTest`
+ * To run this test: `atest WMShellFlickerTestsPip1:AutoEnterPipWithSourceRectHintTest`
*
* Actions:
* ```
@@ -66,9 +66,7 @@
@Test
fun pipOverlayNotShown() {
val overlay = ComponentNameMatcher.PIP_CONTENT_OVERLAY
- flicker.assertLayers {
- this.notContains(overlay)
- }
+ flicker.assertLayers { this.notContains(overlay) }
}
@Presubmit
@Test
@@ -83,4 +81,4 @@
// auto enter and sourceRectHint that causes the app to move outside of the display
// bounds during the transition.
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index 031acf4..a5e0550 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -17,10 +17,10 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.component.ComponentNameMatcher
import com.android.wm.shell.flicker.pip.common.ClosePipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -31,7 +31,7 @@
/**
* Test closing a pip window by swiping it to the bottom-center of the screen
*
- * To run this test: `atest WMShellFlickerTests:ExitPipWithSwipeDownTest`
+ * To run this test: `atest WMShellFlickerTestsPip1:ClosePipBySwipingDownTest`
*
* Actions:
* ```
@@ -69,7 +69,8 @@
wmHelper.currentState.layerState
.getLayerWithBuffer(barComponent)
?.visibleRegion
- ?.height
+ ?.bounds
+ ?.height()
?: error("Couldn't find Nav or Task bar layer")
// The dismiss button doesn't appear at the complete bottom of the screen,
// it appears above the hot seat but `hotseatBarSize` is not available outside
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
index 860307f..d177624 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
@@ -30,7 +30,7 @@
/**
* Test closing a pip window via the dismiss button
*
- * To run this test: `atest WMShellFlickerTests:ExitPipWithDismissButtonTest`
+ * To run this test: `atest WMShellFlickerTestsPip1:ClosePipWithDismissButtonTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
index c554161..a86803d 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -31,7 +31,7 @@
/**
* Test entering pip from an app via [onUserLeaveHint] and by navigating to home.
*
- * To run this test: `atest WMShellFlickerTests:EnterPipOnUserLeaveHintTest`
+ * To run this test: `atest WMShellFlickerTestsPip2:EnterPipOnUserLeaveHintTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index 9a1bd26..a0a61fe2 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -21,12 +21,12 @@
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
import android.tools.flicker.assertions.FlickerTest
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.helpers.WindowUtils
+import android.tools.traces.component.ComponentNameMatcher
import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
@@ -46,7 +46,7 @@
/**
* Test entering pip while changing orientation (from app in landscape to pip window in portrait)
*
- * To run this test: `atest WMShellFlickerTests:EnterPipToOtherOrientationTest`
+ * To run this test: `atest WMShellFlickerTestsPip2:EnterPipToOtherOrientation`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
index f97d8d1..d92f55a 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
@@ -28,7 +28,7 @@
/**
* Test entering pip from an app by interacting with the app UI
*
- * To run this test: `atest WMShellFlickerTests:EnterPipTest`
+ * To run this test: `atest WMShellFlickerTestsPip2:EnterPipViaAppUiButtonTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index 47bf418..8c0817d 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -28,7 +28,7 @@
/**
* Test expanding a pip window back to full screen via the expand button
*
- * To run this test: `atest WMShellFlickerTests:ExitPipViaExpandButtonClickTest`
+ * To run this test: `atest WMShellFlickerTestsPip2:ExitPipToAppViaExpandButtonTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
index a356e68..90a9623 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
@@ -28,7 +28,7 @@
/**
* Test expanding a pip window back to full screen via an intent
*
- * To run this test: `atest WMShellFlickerTests:ExitPipViaIntentTest`
+ * To run this test: `atest WMShellFlickerTestsPip2:ExitPipToAppViaIntentTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 25614ef..9306c77 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -18,11 +18,11 @@
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -33,7 +33,7 @@
/**
* Test expanding a pip window by double-clicking it
*
- * To run this test: `atest WMShellFlickerTests:ExpandPipOnDoubleClickTest`
+ * To run this test: `atest WMShellFlickerTestsPip2:ExpandPipOnDoubleClickTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
index 12e395d..cb8ee27 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
@@ -39,7 +39,7 @@
/**
* Test entering pip from an app via auto-enter property when navigating to home from split screen.
*
- * To run this test: `atest WMShellFlickerTests:AutoEnterPipOnGoToHomeTest`
+ * To run this test: `atest WMShellFlickerTestsPip1:FromSplitScreenAutoEnterPipOnGoToHomeTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
index 5f25d70..f2f10ae 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
@@ -40,7 +40,7 @@
/**
* Test entering pip from an app via auto-enter property when navigating to home from split screen.
*
- * To run this test: `atest WMShellFlickerTests:AutoEnterPipOnGoToHomeTest`
+ * To run this test: `atest WMShellFlickerTestsPip1:FromSplitScreenEnterPipOnUserLeaveHintTest`
*
* Actions:
* ```
@@ -191,8 +191,9 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
index 9b74622..265eb44 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
@@ -32,7 +32,7 @@
/**
* Test Pip movement with Launcher shelf height change (increase).
*
- * To run this test: `atest WMShellFlickerTests:MovePipUpShelfHeightChangeTest`
+ * To run this test: `atest WMShellFlickerTestsPip3:MovePipDownOnShelfHeightChange`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
index e184cf0..04fedf4 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
@@ -19,12 +19,12 @@
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
import android.tools.flicker.assertions.FlickerTest
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.helpers.WindowUtils
+import android.tools.traces.component.ComponentNameMatcher
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.pip.common.PipTransition
@@ -34,7 +34,10 @@
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
-/** Test Pip launch. To run this test: `atest WMShellFlickerTests:PipKeyboardTest` */
+/**
+ * Test Pip launch. To run this test:
+ * `atest WMShellFlickerTestsPip3:MovePipOnImeVisibilityChangeTest`
+ */
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
index 490ebd1..8d6be64 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
@@ -32,7 +32,7 @@
/**
* Test Pip movement with Launcher shelf height change (decrease).
*
- * To run this test: `atest WMShellFlickerTests:MovePipDownShelfHeightChangeTest`
+ * To run this test: `atest WMShellFlickerTestsPip3:MovePipUpOnShelfHeightChangeTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index 6841706..16d08e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -18,11 +18,11 @@
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import android.tools.flicker.subject.exceptions.IncorrectRegionException
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.flicker.subject.exceptions.IncorrectRegionException
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
index 9a6dacb..ed2a0a71 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
@@ -41,8 +41,8 @@
import org.junit.runners.Parameterized
/**
- * Test exiting Pip with orientation changes. To run this test: `atest
- * WMShellFlickerTests:SetRequestedOrientationWhilePinnedTest`
+ * Test exiting Pip with orientation changes. To run this test:
+ * `atest WMShellFlickerTestsPip1:SetRequestedOrientationWhilePinned`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
index d2f803e..9109eaf 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
@@ -35,7 +35,7 @@
/**
* Test Pip Stack in bounds after rotations.
*
- * To run this test: `atest WMShellFlickerTests:PipRotationTest`
+ * To run this test: `atest WMShellFlickerTestsPip1:ShowPipAndRotateDisplay`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
index c9f4a6c..65b60ce 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
@@ -18,12 +18,12 @@
import android.platform.test.annotations.Postsubmit
import android.tools.Rotation
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.flicker.junit.FlickerBuilderProvider
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import org.junit.Test
import org.junit.runners.Parameterized
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
index 8865010..1fc9d99 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
@@ -65,8 +65,8 @@
open class MapsEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
override val standardAppHelper: MapsAppHelper = MapsAppHelper(instrumentation)
- override val permissions: Array<String> = arrayOf(Manifest.permission.POST_NOTIFICATIONS,
- Manifest.permission.ACCESS_FINE_LOCATION)
+ override val permissions: Array<String> =
+ arrayOf(Manifest.permission.POST_NOTIFICATIONS, Manifest.permission.ACCESS_FINE_LOCATION)
val locationManager: LocationManager =
instrumentation.context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
index e85da30..3a0eeb6 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
@@ -19,13 +19,13 @@
import android.Manifest
import android.platform.test.annotations.Postsubmit
import android.tools.Rotation
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.NetflixAppHelper
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.helpers.WindowUtils
+import android.tools.traces.component.ComponentNameMatcher
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
import org.junit.Assume
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
index 3ae5937..35ed8de 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
@@ -18,11 +18,11 @@
import android.Manifest
import android.platform.test.annotations.Postsubmit
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.YouTubeAppHelper
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.component.ComponentNameMatcher
import androidx.test.filters.RequiresDevice
import org.junit.Assume
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
index de8e7c3..879034f 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
@@ -19,13 +19,13 @@
import android.Manifest
import android.platform.test.annotations.Postsubmit
import android.tools.Rotation
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.YouTubeAppHelper
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.helpers.WindowUtils
+import android.tools.traces.component.ComponentNameMatcher
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
import org.junit.Assume
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
index dc12259..8cb81b4 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
@@ -18,10 +18,10 @@
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import android.tools.traces.component.ComponentNameMatcher.Companion.LAUNCHER
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher.Companion.LAUNCHER
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.Test
import org.junit.runners.Parameterized
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
index 3d9eae6..6dd3a17 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
@@ -18,10 +18,10 @@
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
import org.junit.Test
import org.junit.runners.Parameterized
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
index 7b6839d..0742cf9 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
@@ -18,9 +18,9 @@
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.Test
import org.junit.runners.Parameterized
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt
index f4baf5f..c4881e7 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt
@@ -18,9 +18,9 @@
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import android.tools.flicker.subject.region.RegionSubject
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.flicker.subject.region.RegionSubject
import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
import com.android.wm.shell.flicker.utils.Direction
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
index fd467e3..99c1ad2 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
@@ -20,11 +20,11 @@
import android.content.Intent
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import android.tools.helpers.WindowUtils
+import android.tools.traces.component.ComponentNameMatcher
import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.testapp.ActivityOptions
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
index 9e6a686..bcd0f12 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
@@ -24,6 +24,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.MultiWindowUtils
import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
@@ -51,6 +52,10 @@
fun setup() {
Assume.assumeTrue(tapl.isTablet)
+ MultiWindowUtils.executeShellCommand(
+ instrumentation,
+ "settings put system notification_cooldown_enabled 0"
+ )
// Send a notification
sendNotificationApp.launchViaIntent(wmHelper)
sendNotificationApp.postNotification(wmHelper)
@@ -74,5 +79,10 @@
primaryApp.exit(wmHelper)
secondaryApp.exit(wmHelper)
sendNotificationApp.exit(wmHelper)
+
+ MultiWindowUtils.executeShellCommand(
+ instrumentation,
+ "settings reset system notification_cooldown_enabled"
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
index 9312c0a..db962e7 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -141,7 +141,7 @@
private fun isLandscape(rotation: Rotation): Boolean {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
- return displayBounds.width > displayBounds.height
+ return displayBounds.width() > displayBounds.height()
}
private fun isTablet(): Boolean {
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index d74c59e..7f48499 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -17,12 +17,12 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.Presubmit
-import android.tools.traces.component.ComponentNameMatcher
-import android.tools.traces.component.EdgeExtensionComponentMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
+import android.tools.traces.component.EdgeExtensionComponentMatcher
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.CopyContentInSplitBenchmark
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
index 8724346..a72b3d1 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
@@ -18,11 +18,11 @@
import android.platform.test.annotations.Presubmit
import android.tools.NavBar
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.wm.shell.flicker.splitscreen.benchmark.SplitScreenBase
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
index 16d7331..9045364 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
@@ -19,13 +19,13 @@
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.NavBar
-import android.tools.flicker.subject.layers.LayersTraceSubject
-import android.tools.flicker.subject.region.RegionSubject
-import android.tools.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.flicker.subject.layers.LayersTraceSubject
+import android.tools.flicker.subject.region.RegionSubject
+import android.tools.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.UnlockKeyguardToSplitScreenBenchmark
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index 9c5a3fe..7e8e508 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -16,11 +16,11 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index 38206c3..6a6aa1a 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -128,7 +128,7 @@
private fun isLandscape(rotation: Rotation): Boolean {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
- return displayBounds.width > displayBounds.height
+ return displayBounds.width() > displayBounds.height()
}
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
index a19d232..90d2635 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
@@ -17,8 +17,8 @@
package com.android.wm.shell.flicker
import android.app.Instrumentation
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.component.ComponentNameMatcher
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt
index 3df0954..509f4f2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt
@@ -18,13 +18,13 @@
package com.android.wm.shell.flicker.utils
+import android.graphics.Region
import android.tools.Rotation
-import android.tools.datatypes.Region
+import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.subject.layers.LayerTraceEntrySubject
import android.tools.flicker.subject.layers.LayersTraceSubject
-import android.tools.traces.component.IComponentMatcher
-import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.helpers.WindowUtils
+import android.tools.traces.component.IComponentMatcher
fun LegacyFlickerTest.appPairsDividerIsVisibleAtEnd() {
assertLayersEnd { this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT) }
@@ -263,41 +263,41 @@
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return invoke {
val dividerRegion =
- layer(SPLIT_SCREEN_DIVIDER_COMPONENT)?.visibleRegion?.region
+ layer(SPLIT_SCREEN_DIVIDER_COMPONENT)?.visibleRegion?.region?.bounds
?: error("$SPLIT_SCREEN_DIVIDER_COMPONENT component not found")
visibleRegion(component).isNotEmpty()
visibleRegion(component)
.coversAtMost(
- if (displayBounds.width > displayBounds.height) {
+ if (displayBounds.width() > displayBounds.height()) {
if (landscapePosLeft) {
- Region.from(
+ Region(
0,
0,
- (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
- displayBounds.bounds.bottom
+ (dividerRegion.left + dividerRegion.right) / 2,
+ displayBounds.bottom
)
} else {
- Region.from(
- (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
+ Region(
+ (dividerRegion.left + dividerRegion.right) / 2,
0,
- displayBounds.bounds.right,
- displayBounds.bounds.bottom
+ displayBounds.right,
+ displayBounds.bottom
)
}
} else {
if (portraitPosTop) {
- Region.from(
+ Region(
0,
0,
- displayBounds.bounds.right,
- (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2
+ displayBounds.right,
+ (dividerRegion.top + dividerRegion.bottom) / 2
)
} else {
- Region.from(
+ Region(
0,
- (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2,
- displayBounds.bounds.right,
- displayBounds.bounds.bottom
+ (dividerRegion.top + dividerRegion.bottom) / 2,
+ displayBounds.right,
+ displayBounds.bottom
)
}
}
@@ -420,17 +420,17 @@
fun getPrimaryRegion(dividerRegion: Region, rotation: Rotation): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation.isRotated()) {
- Region.from(
+ Region(
0,
0,
dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset,
- displayBounds.bounds.bottom
+ displayBounds.bottom
)
} else {
- Region.from(
+ Region(
0,
0,
- displayBounds.bounds.right,
+ displayBounds.right,
dividerRegion.bounds.top + WindowUtils.dockedStackDividerInset
)
}
@@ -439,18 +439,18 @@
fun getSecondaryRegion(dividerRegion: Region, rotation: Rotation): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation.isRotated()) {
- Region.from(
+ Region(
dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset,
0,
- displayBounds.bounds.right,
- displayBounds.bounds.bottom
+ displayBounds.right,
+ displayBounds.bottom
)
} else {
- Region.from(
+ Region(
0,
dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
- displayBounds.bounds.right,
- displayBounds.bounds.bottom
+ displayBounds.right,
+ displayBounds.bottom
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/ICommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/ICommonAssertions.kt
index 50c0435..4465a16 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/ICommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/ICommonAssertions.kt
@@ -17,8 +17,8 @@
package com.android.wm.shell.flicker.utils
import android.platform.test.annotations.Presubmit
-import android.tools.traces.component.ComponentNameMatcher
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.component.ComponentNameMatcher
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
index 9cc3a98..c4954f9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
@@ -20,11 +20,11 @@
import android.graphics.Point
import android.os.SystemClock
import android.tools.Rotation
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.traces.component.ComponentNameMatcher
import android.tools.traces.component.IComponentMatcher
import android.tools.traces.component.IComponentNameMatcher
-import android.tools.device.apphelpers.StandardAppHelper
-import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.traces.parsers.WindowManagerStateHelper
import android.tools.traces.parsers.toFlickerComponent
import android.view.InputDevice
@@ -182,13 +182,7 @@
val swipeXCoordinate = displayBounds.centerX() / 2
// Pull down the notifications
- device.swipe(
- swipeXCoordinate,
- 5,
- swipeXCoordinate,
- displayBounds.bottom,
- 50 /* steps */
- )
+ device.swipe(swipeXCoordinate, 5, swipeXCoordinate, displayBounds.bottom, 50 /* steps */)
SystemClock.sleep(TIMEOUT_MS)
// Find the target notification
@@ -211,7 +205,7 @@
// Drag to split
val dragStart = notificationContent.visibleCenter
val dragMiddle = Point(dragStart.x + 50, dragStart.y)
- val dragEnd = Point(displayBounds.width / 4, displayBounds.width / 4)
+ val dragEnd = Point(displayBounds.width() / 4, displayBounds.width() / 4)
val downTime = SystemClock.uptimeMillis()
touch(instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime, TIMEOUT_MS, dragStart)
@@ -318,7 +312,7 @@
wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace
?: error("Display not found")
val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
- dividerBar.drag(Point(displayBounds.width * 1 / 3, displayBounds.height * 2 / 3), 200)
+ dividerBar.drag(Point(displayBounds.width() * 1 / 3, displayBounds.height() * 2 / 3), 200)
wmHelper
.StateSyncBuilder()
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 32c0703..13f95cc 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -39,7 +39,7 @@
static_libs: [
"WindowManager-Shell",
"junit",
- "flag-junit-base",
+ "flag-junit",
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
@@ -55,6 +55,9 @@
"platform-test-annotations",
"servicestests-utils",
"com_android_wm_shell_flags_lib",
+ "guava-android-testlib",
+ "com.android.window.flags.window-aconfig-java",
+ "platform-test-annotations",
],
libs: [
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index 3672ae3..24f4d92 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -23,8 +23,10 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
+import android.content.Intent;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
@@ -38,6 +40,7 @@
private WindowContainerToken mToken = createMockWCToken();
private int mParentTaskId = INVALID_TASK_ID;
+ private Intent mBaseIntent = new Intent();
private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD;
private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED;
private int mDisplayId = Display.DEFAULT_DISPLAY;
@@ -68,6 +71,15 @@
return this;
}
+ /**
+ * Set {@link ActivityManager.RunningTaskInfo#baseIntent} for the task info, by default
+ * an empty intent is assigned
+ */
+ public TestRunningTaskInfoBuilder setBaseIntent(@NonNull Intent intent) {
+ mBaseIntent = intent;
+ return this;
+ }
+
public TestRunningTaskInfoBuilder setActivityType(
@WindowConfiguration.ActivityType int activityType) {
mActivityType = activityType;
@@ -109,6 +121,7 @@
public ActivityManager.RunningTaskInfo build() {
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.taskId = sNextTaskId++;
+ info.baseIntent = mBaseIntent;
info.parentTaskId = mParentTaskId;
info.displayId = mDisplayId;
info.configuration.windowConfiguration.setBounds(mBounds);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 9c623bd..65169e3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.back;
-import static android.window.BackNavigationInfo.KEY_TRIGGER_BACK;
+import static android.window.BackNavigationInfo.KEY_NAVIGATION_FINISHED;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -596,6 +596,7 @@
// Set up the monitoring objects.
doNothing().when(runner).onAnimationStart(anyInt(), any(), any(), any(), any());
+ doReturn(false).when(animationRunner).shouldMonitorCUJ(any());
doReturn(runner).when(animationRunner).getRunner();
doReturn(callback).when(animationRunner).getCallback();
@@ -677,7 +678,7 @@
@Override
public void onResult(@Nullable Bundle result) {
mBackNavigationDone = true;
- mTriggerBack = result.getBoolean(KEY_TRIGGER_BACK);
+ mTriggerBack = result.getBoolean(KEY_NAVIGATION_FINISHED);
}
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index 0c45d52..b2b54ac 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -119,6 +119,57 @@
}
@Test
+ fun isOnlyActiveTask_noActiveTasks() {
+ // Not an active task
+ assertThat(repo.isOnlyActiveTask(1)).isFalse()
+ }
+
+ @Test
+ fun isOnlyActiveTask_singleActiveTask() {
+ repo.addActiveTask(DEFAULT_DISPLAY, 1)
+ // The only active task
+ assertThat(repo.isActiveTask(1)).isTrue()
+ assertThat(repo.isOnlyActiveTask(1)).isTrue()
+ // Not an active task
+ assertThat(repo.isActiveTask(99)).isFalse()
+ assertThat(repo.isOnlyActiveTask(99)).isFalse()
+ }
+
+ @Test
+ fun isOnlyActiveTask_multipleActiveTasks() {
+ repo.addActiveTask(DEFAULT_DISPLAY, 1)
+ repo.addActiveTask(DEFAULT_DISPLAY, 2)
+ // Not the only task
+ assertThat(repo.isActiveTask(1)).isTrue()
+ assertThat(repo.isOnlyActiveTask(1)).isFalse()
+ // Not the only task
+ assertThat(repo.isActiveTask(2)).isTrue()
+ assertThat(repo.isOnlyActiveTask(2)).isFalse()
+ // Not an active task
+ assertThat(repo.isActiveTask(99)).isFalse()
+ assertThat(repo.isOnlyActiveTask(99)).isFalse()
+ }
+
+ @Test
+ fun isOnlyActiveTask_multipleDisplays() {
+ repo.addActiveTask(DEFAULT_DISPLAY, 1)
+ repo.addActiveTask(DEFAULT_DISPLAY, 2)
+ repo.addActiveTask(SECOND_DISPLAY, 3)
+ // Not the only task on DEFAULT_DISPLAY
+ assertThat(repo.isActiveTask(1)).isTrue()
+ assertThat(repo.isOnlyActiveTask(1)).isFalse()
+ // Not the only task on DEFAULT_DISPLAY
+ assertThat(repo.isActiveTask(2)).isTrue()
+ assertThat(repo.isOnlyActiveTask(2)).isFalse()
+ // The only active task on SECOND_DISPLAY
+ assertThat(repo.isActiveTask(3)).isTrue()
+ assertThat(repo.isOnlyActiveTask(3)).isTrue()
+ // Not an active task
+ assertThat(repo.isActiveTask(99)).isFalse()
+ assertThat(repo.isOnlyActiveTask(99)).isFalse()
+ }
+
+ @Test
fun addListener_notifiesVisibleFreeformTask() {
repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
val listener = TestVisibilityListener()
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 93a967e..64f6041 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
@@ -23,10 +23,13 @@
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.content.Intent
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
import android.os.Binder
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
@@ -34,11 +37,15 @@
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.DisplayAreaInfo
import android.window.RemoteTransition
import android.window.TransitionRequestInfo
+import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
@@ -161,6 +168,10 @@
(i.arguments.first() as Rect).set(STABLE_BOUNDS)
}
+ val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
+
controller = createController()
controller.setSplitScreenController(splitScreenController)
@@ -219,7 +230,8 @@
}
@Test
- fun showDesktopApps_allAppsInvisible_bringsToFront() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperDisabled() {
val homeTask = setUpHomeTask()
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
@@ -238,7 +250,27 @@
}
@Test
- fun showDesktopApps_appsAlreadyVisible_bringsToFront() {
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperEnabled() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskHidden(task1)
+ markTaskHidden(task2)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: wallpaper intent, task1, task2
+ wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperDisabled() {
val homeTask = setUpHomeTask()
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
@@ -257,7 +289,27 @@
}
@Test
- fun showDesktopApps_someAppsInvisible_reordersAll() {
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperEnabled() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskVisible(task1)
+ markTaskVisible(task2)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: wallpaper intent, task1, task2
+ wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperDisabled() {
val homeTask = setUpHomeTask()
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
@@ -276,7 +328,27 @@
}
@Test
- fun showDesktopApps_noActiveTasks_reorderHomeToTop() {
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperEnabled() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskHidden(task1)
+ markTaskVisible(task2)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: wallpaper intent, task1, task2
+ wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_noActiveTasks_reorderHomeToTop_desktopWallpaperDisabled() {
val homeTask = setUpHomeTask()
controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
@@ -288,7 +360,18 @@
}
@Test
- fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay() {
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_noActiveTasks_addDesktopWallpaper_desktopWallpaperEnabled() {
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperDisabled() {
val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
setUpHomeTask(SECOND_DISPLAY)
@@ -307,6 +390,25 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperEnabled() {
+ val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
+ setUpHomeTask(SECOND_DISPLAY)
+ val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY)
+ markTaskHidden(taskDefaultDisplay)
+ markTaskHidden(taskSecondDisplay)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(2)
+ // Expect order to be from bottom: wallpaper intent, task
+ wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ wct.assertReorderAt(index = 1, taskDefaultDisplay)
+ }
+
+ @Test
fun getVisibleTaskCount_noTasks_returnsZero() {
assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
}
@@ -336,9 +438,10 @@
}
@Test
- fun moveToDesktop_displayFullscreen_windowingModeSetToFreeform() {
+ fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() {
val task = setUpFullscreenTask()
- task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
controller.moveToDesktop(task)
val wct = getLatestMoveToDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -346,9 +449,10 @@
}
@Test
- fun moveToDesktop_displayFreeform_windowingModeSetToUndefined() {
+ fun moveToDesktop_tdaFreeform_windowingModeSetToUndefined() {
val task = setUpFullscreenTask()
- task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
controller.moveToDesktop(task)
val wct = getLatestMoveToDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -413,7 +517,8 @@
}
@Test
- fun moveToDesktop_otherFreeformTasksBroughtToFront() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperDisabled() {
val homeTask = setUpHomeTask()
val freeformTask = setUpFreeformTask()
val fullscreenTask = setUpFullscreenTask()
@@ -431,6 +536,26 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperEnabled() {
+ val freeformTask = setUpFreeformTask()
+ val fullscreenTask = setUpFullscreenTask()
+ markTaskHidden(freeformTask)
+
+ controller.moveToDesktop(fullscreenTask)
+
+ with(getLatestMoveToDesktopWct()) {
+ // Operations should include wallpaper intent, freeform task, fullscreen task
+ assertThat(hierarchyOps).hasSize(3)
+ assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ assertReorderAt(index = 1, freeformTask)
+ assertReorderAt(index = 2, fullscreenTask)
+ assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+ }
+
+ @Test
fun moveToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() {
setUpHomeTask(displayId = DEFAULT_DISPLAY)
val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -481,9 +606,10 @@
}
@Test
- fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() {
+ fun moveToFullscreen_tdaFullscreen_windowingModeSetToUndefined() {
val task = setUpFreeformTask()
- task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
controller.moveToFullscreen(task.taskId)
val wct = getLatestExitDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -491,9 +617,10 @@
}
@Test
- fun moveToFullscreen_displayFreeform_windowingModeSetToFullscreen() {
+ fun moveToFullscreen_tdaFreeform_windowingModeSetToFullscreen() {
val task = setUpFreeformTask()
- task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
controller.moveToFullscreen(task.taskId)
val wct = getLatestExitDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -595,6 +722,48 @@
}
@Test
+ fun onDesktopWindowClose_noActiveTasks() {
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, 1 /* taskId */)
+ // Doesn't modify transaction
+ assertThat(wct.hierarchyOps).isEmpty()
+ }
+
+ @Test
+ fun onDesktopWindowClose_singleActiveTask_noWallpaperActivityToken() {
+ val task = setUpFreeformTask()
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, task.taskId)
+ // Doesn't modify transaction
+ assertThat(wct.hierarchyOps).isEmpty()
+ }
+
+ @Test
+ fun onDesktopWindowClose_singleActiveTask_hasWallpaperActivityToken() {
+ val task = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, task.taskId)
+ // Adds remove wallpaper operation
+ wct.assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ fun onDesktopWindowClose_multipleActiveTasks() {
+ val task1 = setUpFreeformTask()
+ setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, task1.taskId)
+ // Doesn't modify transaction
+ assertThat(wct.hierarchyOps).isEmpty()
+ }
+
+ @Test
fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
@@ -638,6 +807,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_fullscreenTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
@@ -684,7 +854,7 @@
createTransition(freeformTask2, type = TRANSIT_TO_FRONT)
)
assertThat(result?.changes?.get(freeformTask2.token.asBinder())?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
}
@Test
@@ -694,7 +864,7 @@
val task = createFreeformTask()
val result = controller.handleRequest(Binder(), createTransition(task))
assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
}
@Test
@@ -706,10 +876,11 @@
val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
assertThat(result?.changes?.get(taskDefaultDisplay.token.asBinder())?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_freeformTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
@@ -792,7 +963,56 @@
val result = controller.handleRequest(Binder(), createTransition(task))
assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_singleActiveTask_noToken() {
+ val task = setUpFreeformTask()
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+ // Doesn't handle request
+ assertThat(result).isNull()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_singleActiveTask_hasToken_desktopWallpaperDisabled() {
+ desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+
+ val task = setUpFreeformTask()
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+ // Doesn't handle request
+ assertThat(result).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_singleActiveTask_hasToken_desktopWallpaperEnabled() {
+ val wallpaperToken = MockToken().token()
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+
+ val task = setUpFreeformTask()
+ val result =
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+ assertThat(result).isNotNull()
+ // Creates remove wallpaper transaction
+ result!!.assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_multipleActiveTasks() {
+ desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+
+ val task1 = setUpFreeformTask()
+ setUpFreeformTask()
+ val result =
+ controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+ // Doesn't handle request
+ assertThat(result).isNull()
}
@Test
@@ -895,7 +1115,7 @@
val wct = getLatestExitDesktopWct()
assertThat(wct.changes[task2.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
}
@Test
@@ -998,6 +1218,9 @@
assertThat(desktopModeTaskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull()
}
+ private val desktopWallpaperIntent: Intent
+ get() = Intent(context, DesktopWallpaperActivity::class.java)
+
private fun setUpFreeformTask(
displayId: Int = DEFAULT_DISPLAY,
bounds: Rect? = null
@@ -1123,10 +1346,14 @@
}
}
-private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) {
+private fun WindowContainerTransaction.assertIndexInBounds(index: Int) {
assertWithMessage("WCT does not have a hierarchy operation at index $index")
.that(hierarchyOps.size)
.isGreaterThan(index)
+}
+
+private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) {
+ assertIndexInBounds(index)
val op = hierarchyOps[index]
assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REORDER)
assertThat(op.container).isEqualTo(task.token.asBinder())
@@ -1137,3 +1364,17 @@
assertReorderAt(i, tasks[i])
}
}
+
+private fun WindowContainerTransaction.assertRemoveAt(index: Int, token: WindowContainerToken) {
+ assertIndexInBounds(index)
+ val op = hierarchyOps[index]
+ assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+ assertThat(op.container).isEqualTo(token.asBinder())
+}
+
+private fun WindowContainerTransaction.assertPendingIntentAt(index: Int, intent: Intent) {
+ assertIndexInBounds(index)
+ val op = hierarchyOps[index]
+ assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
+ assertThat(op.pendingIntent?.intent?.component).isEqualTo(intent.component)
+}
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 d38e97f..40b59c1 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
@@ -35,6 +35,7 @@
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -45,16 +46,21 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Bundle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
@@ -70,6 +76,7 @@
import com.android.wm.shell.util.SplitBounds;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -99,6 +106,11 @@
private ActivityTaskManager mActivityTaskManager;
@Mock
private DisplayInsetsController mDisplayInsetsController;
+ @Mock
+ private IRecentTasksListener mRecentTasksListener;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private ShellTaskOrganizer mShellTaskOrganizer;
private RecentTasksController mRecentTasksController;
@@ -426,6 +438,85 @@
}
@Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS})
+ public void onTaskAdded_desktopModeRunningAppsEnabled_triggersOnRunningTaskAppeared()
+ throws Exception {
+ mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+ ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10);
+
+ mRecentTasksControllerReal.onTaskAdded(taskInfo);
+
+ verify(mRecentTasksListener).onRunningTaskAppeared(taskInfo);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS)
+ public void onTaskAdded_desktopModeRunningAppsDisabled_doesNotTriggerOnRunningTaskAppeared()
+ throws Exception {
+ mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+ ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10);
+
+ mRecentTasksControllerReal.onTaskAdded(taskInfo);
+
+ verify(mRecentTasksListener, never()).onRunningTaskAppeared(any());
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS})
+ public void taskWindowingModeChanged_desktopRunningAppsEnabled_triggersOnRunningTaskChanged()
+ throws Exception {
+ mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+ ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10);
+
+ mRecentTasksControllerReal.onTaskWindowingModeChanged(taskInfo);
+
+ verify(mRecentTasksListener).onRunningTaskChanged(taskInfo);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS)
+ public void
+ taskWindowingModeChanged_desktopRunningAppsDisabled_doesNotTriggerOnRunningTaskChanged()
+ throws Exception {
+ mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+ ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10);
+
+ mRecentTasksControllerReal.onTaskWindowingModeChanged(taskInfo);
+
+ verify(mRecentTasksListener, never()).onRunningTaskChanged(any());
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS})
+ public void onTaskRemoved_desktopModeRunningAppsEnabled_triggersOnRunningTaskVanished()
+ throws Exception {
+ mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+ ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10);
+
+ mRecentTasksControllerReal.onTaskRemoved(taskInfo);
+
+ verify(mRecentTasksListener).onRunningTaskVanished(taskInfo);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS)
+ public void onTaskRemoved_desktopModeRunningAppsDisabled_doesNotTriggerOnRunningTaskVanished()
+ throws Exception {
+ mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+ ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10);
+
+ mRecentTasksControllerReal.onTaskRemoved(taskInfo);
+
+ verify(mRecentTasksListener, never()).onRunningTaskVanished(any());
+ }
+
+ @Test
public void getNullSplitBoundsNonSplitTask() {
SplitBounds sb = mRecentTasksController.getSplitBoundsForTaskId(3);
assertNull("splitBounds should be null for non-split task", sb);
@@ -471,6 +562,7 @@
private ActivityManager.RunningTaskInfo makeRunningTaskInfo(int taskId) {
ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.taskId = taskId;
+ info.realActivity = new ComponentName("testPackage", "testClass");
return info;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index e7d37ad..0db10ef 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -24,6 +24,7 @@
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
@@ -167,6 +168,25 @@
}
@Test
+ public void testStartDragToDesktopDoesNotTriggerCallback() throws RemoteException {
+ TransitionInfo info = mock(TransitionInfo.class);
+ TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+ ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+ when(change.getTaskInfo()).thenReturn(taskInfo);
+ when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+ when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+
+ setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+
+ mHomeTransitionObserver.onTransitionReady(mock(IBinder.class),
+ info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+
+ verify(mListener, times(0)).onHomeVisibilityChanged(anyBoolean());
+ }
+
+ @Test
public void testHomeActivityWithBackGestureNotifiesHomeIsVisible() throws RemoteException {
TransitionInfo info = mock(TransitionInfo.class);
TransitionInfo.Change change = mock(TransitionInfo.Change.class);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index e9da258..2366917 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -83,6 +83,7 @@
import android.window.IRemoteTransitionFinishedCallback;
import android.window.IWindowContainerToken;
import android.window.RemoteTransition;
+import android.window.RemoteTransitionStub;
import android.window.TransitionFilter;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -280,7 +281,7 @@
final boolean[] remoteCalled = new boolean[]{false};
final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction();
- IRemoteTransition testRemote = new IRemoteTransition.Stub() {
+ IRemoteTransition testRemote = new RemoteTransitionStub() {
@Override
public void startAnimation(IBinder token, TransitionInfo info,
SurfaceControl.Transaction t,
@@ -288,16 +289,6 @@
remoteCalled[0] = true;
finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
}
-
- @Override
- public void mergeAnimation(IBinder token, TransitionInfo info,
- SurfaceControl.Transaction t, IBinder mergeTarget,
- IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
- }
-
- @Override
- public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException {
- }
};
IBinder transitToken = new Binder();
transitions.requestStartTransition(transitToken,
@@ -450,7 +441,7 @@
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
final boolean[] remoteCalled = new boolean[]{false};
- IRemoteTransition testRemote = new IRemoteTransition.Stub() {
+ IRemoteTransition testRemote = new RemoteTransitionStub() {
@Override
public void startAnimation(IBinder token, TransitionInfo info,
SurfaceControl.Transaction t,
@@ -458,16 +449,6 @@
remoteCalled[0] = true;
finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
}
-
- @Override
- public void mergeAnimation(IBinder token, TransitionInfo info,
- SurfaceControl.Transaction t, IBinder mergeTarget,
- IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
- }
-
- @Override
- public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException {
- }
};
TransitionFilter filter = new TransitionFilter();
@@ -500,7 +481,7 @@
final boolean[] remoteCalled = new boolean[]{false};
final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction();
- IRemoteTransition testRemote = new IRemoteTransition.Stub() {
+ IRemoteTransition testRemote = new RemoteTransitionStub() {
@Override
public void startAnimation(IBinder token, TransitionInfo info,
SurfaceControl.Transaction t,
@@ -508,16 +489,6 @@
remoteCalled[0] = true;
finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
}
-
- @Override
- public void mergeAnimation(IBinder token, TransitionInfo info,
- SurfaceControl.Transaction t, IBinder mergeTarget,
- IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
- }
-
- @Override
- public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException {
- }
};
final int transitType = TRANSIT_FIRST_CUSTOM + 1;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java
index 87330d2..184e895 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java
@@ -20,6 +20,7 @@
import android.view.SurfaceControl;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
+import android.window.RemoteTransitionStub;
import android.window.TransitionInfo;
import android.window.WindowContainerTransaction;
@@ -29,7 +30,7 @@
* {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction,
* IRemoteTransitionFinishedCallback)} being called.
*/
-public class TestRemoteTransition extends IRemoteTransition.Stub {
+public class TestRemoteTransition extends RemoteTransitionStub {
private boolean mCalled = false;
private boolean mConsumed = false;
final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction();
@@ -44,12 +45,6 @@
}
@Override
- public void mergeAnimation(IBinder transition, TransitionInfo info,
- SurfaceControl.Transaction t, IBinder mergeTarget,
- IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
- }
-
- @Override
public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException {
mConsumed = true;
}
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 c5e229f..acc0bce 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
@@ -298,6 +298,32 @@
}
@Test
+ public void fold_animationInProgress_finishesTransition() {
+ TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+ TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+
+ // Unfold
+ mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ false);
+ mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+ mUnfoldTransitionHandler.startAnimation(
+ mTransition,
+ mock(TransitionInfo.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ finishCallback
+ );
+
+ // Start animation but don't finish it
+ mShellUnfoldProgressProvider.onStateChangeStarted();
+ mShellUnfoldProgressProvider.onStateChangeProgress(0.5f);
+
+ // Fold
+ mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ true);
+
+ verify(finishCallback).onTransitionFinished(any());
+ }
+
+ @Test
public void mergeAnimation_eatsDisplayOnlyTransitions() {
TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
new file mode 100644
index 0000000..82e5a1c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor;
+
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.NonNull;
+import android.graphics.Point;
+import android.graphics.Region;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.testing.AndroidTestingRunner;
+import android.util.Size;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.window.flags.Flags;
+
+import com.google.common.testing.EqualsTester;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link DragResizeWindowGeometry}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:DragResizeWindowGeometryTests
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DragResizeWindowGeometryTests {
+ private static final Size TASK_SIZE = new Size(500, 1000);
+ private static final int TASK_CORNER_RADIUS = 10;
+ private static final int EDGE_RESIZE_THICKNESS = 15;
+ private static final int FINE_CORNER_SIZE = EDGE_RESIZE_THICKNESS * 2 + 10;
+ private static final int LARGE_CORNER_SIZE = FINE_CORNER_SIZE + 10;
+ private static final DragResizeWindowGeometry GEOMETRY = new DragResizeWindowGeometry(
+ TASK_CORNER_RADIUS, TASK_SIZE, EDGE_RESIZE_THICKNESS, FINE_CORNER_SIZE,
+ LARGE_CORNER_SIZE);
+ // Points in the edge resize handle. Note that coordinates start from the top left.
+ private static final Point TOP_EDGE_POINT = new Point(TASK_SIZE.getWidth() / 2,
+ -EDGE_RESIZE_THICKNESS / 2);
+ private static final Point LEFT_EDGE_POINT = new Point(-EDGE_RESIZE_THICKNESS / 2,
+ TASK_SIZE.getHeight() / 2);
+ private static final Point RIGHT_EDGE_POINT = new Point(
+ TASK_SIZE.getWidth() + EDGE_RESIZE_THICKNESS / 2, TASK_SIZE.getHeight() / 2);
+ private static final Point BOTTOM_EDGE_POINT = new Point(TASK_SIZE.getWidth() / 2,
+ TASK_SIZE.getHeight() + EDGE_RESIZE_THICKNESS / 2);
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ /**
+ * Check that both groups of objects satisfy equals/hashcode within each group, and that each
+ * group is distinct from the next.
+ */
+ @Test
+ public void testEqualsAndHash() {
+ new EqualsTester()
+ .addEqualityGroup(
+ GEOMETRY,
+ new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
+ EDGE_RESIZE_THICKNESS, FINE_CORNER_SIZE, LARGE_CORNER_SIZE))
+ .addEqualityGroup(
+ new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
+ EDGE_RESIZE_THICKNESS + 10, FINE_CORNER_SIZE, LARGE_CORNER_SIZE),
+ new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
+ EDGE_RESIZE_THICKNESS + 10, FINE_CORNER_SIZE, LARGE_CORNER_SIZE))
+ .addEqualityGroup(
+ new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
+ EDGE_RESIZE_THICKNESS + 10, FINE_CORNER_SIZE,
+ LARGE_CORNER_SIZE + 5),
+ new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
+ EDGE_RESIZE_THICKNESS + 10, FINE_CORNER_SIZE,
+ LARGE_CORNER_SIZE + 5))
+ .testEquals();
+ }
+
+ @Test
+ public void testGetTaskSize() {
+ assertThat(GEOMETRY.getTaskSize()).isEqualTo(TASK_SIZE);
+ }
+
+ @Test
+ public void testRegionUnionContainsEdges() {
+ Region region = new Region();
+ GEOMETRY.union(region);
+ assertThat(region.isComplex()).isTrue();
+ // Region excludes task area. Note that coordinates start from top left.
+ assertThat(region.contains(TASK_SIZE.getWidth() / 2, TASK_SIZE.getHeight() / 2)).isFalse();
+ // Region includes edges outside the task window.
+ verifyVerticalEdge(region, LEFT_EDGE_POINT);
+ verifyHorizontalEdge(region, TOP_EDGE_POINT);
+ verifyVerticalEdge(region, RIGHT_EDGE_POINT);
+ verifyHorizontalEdge(region, BOTTOM_EDGE_POINT);
+ }
+
+ private static void verifyHorizontalEdge(@NonNull Region region, @NonNull Point point) {
+ assertThat(region.contains(point.x, point.y)).isTrue();
+ // Horizontally along the edge is still contained.
+ assertThat(region.contains(point.x + EDGE_RESIZE_THICKNESS, point.y)).isTrue();
+ assertThat(region.contains(point.x - EDGE_RESIZE_THICKNESS, point.y)).isTrue();
+ // Vertically along the edge is not contained.
+ assertThat(region.contains(point.x, point.y - EDGE_RESIZE_THICKNESS)).isFalse();
+ assertThat(region.contains(point.x, point.y + EDGE_RESIZE_THICKNESS)).isFalse();
+ }
+
+ private static void verifyVerticalEdge(@NonNull Region region, @NonNull Point point) {
+ assertThat(region.contains(point.x, point.y)).isTrue();
+ // Horizontally along the edge is not contained.
+ assertThat(region.contains(point.x + EDGE_RESIZE_THICKNESS, point.y)).isFalse();
+ assertThat(region.contains(point.x - EDGE_RESIZE_THICKNESS, point.y)).isFalse();
+ // Vertically along the edge is contained.
+ assertThat(region.contains(point.x, point.y - EDGE_RESIZE_THICKNESS)).isTrue();
+ assertThat(region.contains(point.x, point.y + EDGE_RESIZE_THICKNESS)).isTrue();
+ }
+
+ /**
+ * Validate that with the flag enabled, the corner resize regions are the largest size, to
+ * capture all eligible input regardless of source (touch or cursor).
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ public void testRegionUnion_edgeDragResizeEnabled_containsLargeCorners() {
+ Region region = new Region();
+ GEOMETRY.union(region);
+ final int cornerRadius = LARGE_CORNER_SIZE / 2;
+
+ new TestPoints(TASK_SIZE, cornerRadius).validateRegion(region);
+ }
+
+ /**
+ * Validate that with the flag disabled, the corner resize regions are the original smaller
+ * size.
+ */
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ public void testRegionUnion_edgeDragResizeDisabled_containsFineCorners() {
+ Region region = new Region();
+ GEOMETRY.union(region);
+ final int cornerRadius = FINE_CORNER_SIZE / 2;
+
+ new TestPoints(TASK_SIZE, cornerRadius).validateRegion(region);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ public void testCalculateControlType_edgeDragResizeEnabled_edges() {
+ // The input source (touch or cursor) shouldn't impact the edge resize size.
+ validateCtrlTypeForEdges(/* isTouch= */ false);
+ validateCtrlTypeForEdges(/* isTouch= */ true);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ public void testCalculateControlType_edgeDragResizeDisabled_edges() {
+ // Edge resizing is not supported when the flag is disabled.
+ validateCtrlTypeForEdges(/* isTouch= */ false);
+ validateCtrlTypeForEdges(/* isTouch= */ false);
+ }
+
+ private void validateCtrlTypeForEdges(boolean isTouch) {
+ assertThat(GEOMETRY.calculateCtrlType(isTouch, LEFT_EDGE_POINT.x,
+ LEFT_EDGE_POINT.y)).isEqualTo(CTRL_TYPE_LEFT);
+ assertThat(GEOMETRY.calculateCtrlType(isTouch, TOP_EDGE_POINT.x,
+ TOP_EDGE_POINT.y)).isEqualTo(CTRL_TYPE_TOP);
+ assertThat(GEOMETRY.calculateCtrlType(isTouch, RIGHT_EDGE_POINT.x,
+ RIGHT_EDGE_POINT.y)).isEqualTo(CTRL_TYPE_RIGHT);
+ assertThat(GEOMETRY.calculateCtrlType(isTouch, BOTTOM_EDGE_POINT.x,
+ BOTTOM_EDGE_POINT.y)).isEqualTo(CTRL_TYPE_BOTTOM);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ public void testCalculateControlType_edgeDragResizeEnabled_corners() {
+ final TestPoints fineTestPoints = new TestPoints(TASK_SIZE, FINE_CORNER_SIZE / 2);
+ final TestPoints largeCornerTestPoints = new TestPoints(TASK_SIZE, LARGE_CORNER_SIZE / 2);
+
+ // When the flag is enabled, points within fine corners should pass regardless of touch or
+ // not. Points outside fine corners should not pass when using a course input (non-touch).
+ fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ true, true);
+ fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ true, true);
+ fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ false, true);
+ fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ false, false);
+
+ // When the flag is enabled, points near the large corners should only pass when the point
+ // is within the corner for large touch inputs.
+ largeCornerTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ true, true);
+ largeCornerTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ true,
+ false);
+ largeCornerTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ false, false);
+ largeCornerTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ false,
+ false);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ public void testCalculateControlType_edgeDragResizeDisabled_corners() {
+ final TestPoints fineTestPoints = new TestPoints(TASK_SIZE, FINE_CORNER_SIZE / 2);
+ final TestPoints largeCornerTestPoints = new TestPoints(TASK_SIZE, LARGE_CORNER_SIZE / 2);
+
+ // When the flag is disabled, points within fine corners should pass only when touch.
+ fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ true, true);
+ fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ true, false);
+ fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ false, false);
+ fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ false, false);
+
+ // When the flag is disabled, points near the large corners should never pass.
+ largeCornerTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ true, false);
+ largeCornerTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ true,
+ false);
+ largeCornerTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ false, false);
+ largeCornerTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ false,
+ false);
+ }
+
+ /**
+ * Class for creating points for testing the drag resize corners.
+ *
+ * <p>Creates points that are both just within the bounds of each corner, and just outside.
+ */
+ private static final class TestPoints {
+ private final Point mTopLeftPoint;
+ private final Point mTopLeftPointOutside;
+ private final Point mTopRightPoint;
+ private final Point mTopRightPointOutside;
+ private final Point mBottomLeftPoint;
+ private final Point mBottomLeftPointOutside;
+ private final Point mBottomRightPoint;
+ private final Point mBottomRightPointOutside;
+
+ TestPoints(@NonNull Size taskSize, int cornerRadius) {
+ // Point just inside corner square is included.
+ mTopLeftPoint = new Point(-cornerRadius + 1, -cornerRadius + 1);
+ // Point just outside corner square is excluded.
+ mTopLeftPointOutside = new Point(mTopLeftPoint.x - 5, mTopLeftPoint.y - 5);
+
+ mTopRightPoint = new Point(taskSize.getWidth() + cornerRadius - 1, -cornerRadius + 1);
+ mTopRightPointOutside = new Point(mTopRightPoint.x + 5, mTopRightPoint.y - 5);
+
+ mBottomLeftPoint = new Point(-cornerRadius + 1,
+ taskSize.getHeight() + cornerRadius - 1);
+ mBottomLeftPointOutside = new Point(mBottomLeftPoint.x - 5, mBottomLeftPoint.y + 5);
+
+ mBottomRightPoint = new Point(taskSize.getWidth() + cornerRadius - 1,
+ taskSize.getHeight() + cornerRadius - 1);
+ mBottomRightPointOutside = new Point(mBottomRightPoint.x + 5, mBottomRightPoint.y + 5);
+ }
+
+ /**
+ * Validates that all test points are either within or without the given region.
+ */
+ public void validateRegion(@NonNull Region region) {
+ // Point just inside corner square is included.
+ assertThat(region.contains(mTopLeftPoint.x, mTopLeftPoint.y)).isTrue();
+ // Point just outside corner square is excluded.
+ assertThat(region.contains(mTopLeftPointOutside.x, mTopLeftPointOutside.y)).isFalse();
+
+ assertThat(region.contains(mTopRightPoint.x, mTopRightPoint.y)).isTrue();
+ assertThat(
+ region.contains(mTopRightPointOutside.x, mTopRightPointOutside.y)).isFalse();
+
+ assertThat(region.contains(mBottomLeftPoint.x, mBottomLeftPoint.y)).isTrue();
+ assertThat(region.contains(mBottomLeftPointOutside.x,
+ mBottomLeftPointOutside.y)).isFalse();
+
+ assertThat(region.contains(mBottomRightPoint.x, mBottomRightPoint.y)).isTrue();
+ assertThat(region.contains(mBottomRightPointOutside.x,
+ mBottomRightPointOutside.y)).isFalse();
+ }
+
+ /**
+ * Validates that all test points within this drag corner size give the correct
+ * {@code @DragPositioningCallback.CtrlType}.
+ */
+ public void validateCtrlTypeForInnerPoints(@NonNull DragResizeWindowGeometry geometry,
+ boolean isTouch, boolean expectedWithinGeometry) {
+ assertThat(geometry.calculateCtrlType(isTouch, mTopLeftPoint.x,
+ mTopLeftPoint.y)).isEqualTo(
+ expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
+ assertThat(geometry.calculateCtrlType(isTouch, mTopRightPoint.x,
+ mTopRightPoint.y)).isEqualTo(
+ expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
+ assertThat(geometry.calculateCtrlType(isTouch, mBottomLeftPoint.x,
+ mBottomLeftPoint.y)).isEqualTo(
+ expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM
+ : CTRL_TYPE_UNDEFINED);
+ assertThat(geometry.calculateCtrlType(isTouch, mBottomRightPoint.x,
+ mBottomRightPoint.y)).isEqualTo(
+ expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM
+ : CTRL_TYPE_UNDEFINED);
+ }
+
+ /**
+ * Validates that all test points outside this drag corner size give the correct
+ * {@code @DragPositioningCallback.CtrlType}.
+ */
+ public void validateCtrlTypeForOutsidePoints(@NonNull DragResizeWindowGeometry geometry,
+ boolean isTouch, boolean expectedWithinGeometry) {
+ assertThat(geometry.calculateCtrlType(isTouch, mTopLeftPointOutside.x,
+ mTopLeftPointOutside.y)).isEqualTo(
+ expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
+ assertThat(geometry.calculateCtrlType(isTouch, mTopRightPointOutside.x,
+ mTopRightPointOutside.y)).isEqualTo(
+ expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
+ assertThat(geometry.calculateCtrlType(isTouch, mBottomLeftPointOutside.x,
+ mBottomLeftPointOutside.y)).isEqualTo(
+ expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM
+ : CTRL_TYPE_UNDEFINED);
+ assertThat(geometry.calculateCtrlType(isTouch, mBottomRightPointOutside.x,
+ mBottomRightPointOutside.y)).isEqualTo(
+ expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM
+ : CTRL_TYPE_UNDEFINED);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 228b25c..5464937 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -61,7 +61,6 @@
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
-import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager.LayoutParams;
import android.window.SurfaceSyncGroup;
@@ -252,16 +251,14 @@
argThat(lp -> lp.height == 64
&& lp.width == 300
&& (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
- if (ViewRootImpl.CAPTION_ON_SHELL) {
- verify(mMockView).setTaskFocusState(true);
- verify(mMockWindowContainerTransaction).addInsetsSource(
- eq(taskInfo.token),
- any(),
- eq(0 /* index */),
- eq(WindowInsets.Type.captionBar()),
- eq(new Rect(100, 300, 400, 364)),
- any());
- }
+ verify(mMockView).setTaskFocusState(true);
+ verify(mMockWindowContainerTransaction).addInsetsSource(
+ eq(taskInfo.token),
+ any(),
+ eq(0 /* index */),
+ eq(WindowInsets.Type.captionBar()),
+ eq(new Rect(100, 300, 400, 364)),
+ any());
verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index 9d4b426..839c7b6 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -119,30 +119,41 @@
* appear to be bogus.
*/
bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod,
+ uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset,
+ uint32_t* pModWhen, uint32_t* pCrc32) const
+{
+ return getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, pModWhen,
+ pCrc32, nullptr);
+}
+
+bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod,
uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset,
- uint32_t* pModWhen, uint32_t* pCrc32) const
+ uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const
{
const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
const ZipEntry& ze = zipEntry->entry;
- if (pMethod != NULL) {
+ if (pMethod != nullptr) {
*pMethod = ze.method;
}
- if (pUncompLen != NULL) {
+ if (pUncompLen != nullptr) {
*pUncompLen = ze.uncompressed_length;
}
- if (pCompLen != NULL) {
+ if (pCompLen != nullptr) {
*pCompLen = ze.compressed_length;
}
- if (pOffset != NULL) {
+ if (pOffset != nullptr) {
*pOffset = ze.offset;
}
- if (pModWhen != NULL) {
+ if (pModWhen != nullptr) {
*pModWhen = ze.mod_time;
}
- if (pCrc32 != NULL) {
+ if (pCrc32 != nullptr) {
*pCrc32 = ze.crc32;
}
+ if (pExtraFieldSize != nullptr) {
+ *pExtraFieldSize = ze.extra_field_size;
+ }
return true;
}
@@ -310,3 +321,7 @@
return true;
}
+
+const char* ZipFileRO::getZipFileName() {
+ return mFileName;
+}
diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
index be1f98f..f7c5007 100644
--- a/libs/androidfw/include/androidfw/ZipFileRO.h
+++ b/libs/androidfw/include/androidfw/ZipFileRO.h
@@ -151,6 +151,10 @@
uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen,
uint32_t* pCrc32) const;
+ bool getEntryInfo(ZipEntryRO entry, uint16_t* pMethod,
+ uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset,
+ uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const;
+
/*
* Create a new FileMap object that maps a subset of the archive. For
* an uncompressed entry this effectively provides a pointer to the
@@ -187,6 +191,8 @@
*/
bool uncompressEntry(ZipEntryRO entry, int fd) const;
+ const char* getZipFileName();
+
~ZipFileRO();
private:
diff --git a/libs/hostgraphics/ANativeWindow.cpp b/libs/hostgraphics/ANativeWindow.cpp
new file mode 100644
index 0000000..fcfaf02
--- /dev/null
+++ b/libs/hostgraphics/ANativeWindow.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <system/window.h>
+
+static int32_t query(ANativeWindow* window, int what) {
+ int value;
+ int res = window->query(window, what, &value);
+ return res < 0 ? res : value;
+}
+
+static int64_t query64(ANativeWindow* window, int what) {
+ int64_t value;
+ int res = window->perform(window, what, &value);
+ return res < 0 ? res : value;
+}
+
+int ANativeWindow_setCancelBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_cancelBufferInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setDequeueBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_dequeueBufferInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setQueueBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_queueBufferInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setPerformInterceptor(ANativeWindow* window,
+ ANativeWindow_performInterceptor interceptor, void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd) {
+ return window->dequeueBuffer(window, buffer, fenceFd);
+}
+
+int ANativeWindow_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ return window->cancelBuffer(window, buffer, fenceFd);
+}
+
+int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout) {
+ return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, timeout);
+}
+
+// extern "C", so that it can be used outside libhostgraphics (in host hwui/.../CanvasContext.cpp)
+extern "C" void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
+ if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+ return;
+ }
+ window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
+}
+
+int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window) {
+ return query64(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_START);
+}
+
+int64_t ANativeWindow_getLastDequeueDuration(ANativeWindow* window) {
+ return query64(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION);
+}
+
+int64_t ANativeWindow_getLastQueueDuration(ANativeWindow* window) {
+ return query64(window, NATIVE_WINDOW_GET_LAST_QUEUE_DURATION);
+}
+
+int32_t ANativeWindow_getWidth(ANativeWindow* window) {
+ return query(window, NATIVE_WINDOW_WIDTH);
+}
+
+int32_t ANativeWindow_getHeight(ANativeWindow* window) {
+ return query(window, NATIVE_WINDOW_HEIGHT);
+}
+
+int32_t ANativeWindow_getFormat(ANativeWindow* window) {
+ return query(window, NATIVE_WINDOW_FORMAT);
+}
+
+void ANativeWindow_acquire(ANativeWindow* window) {
+ // incStrong/decStrong token must be the same, doesn't matter what it is
+ window->incStrong((void*)ANativeWindow_acquire);
+}
+
+void ANativeWindow_release(ANativeWindow* window) {
+ // incStrong/decStrong token must be the same, doesn't matter what it is
+ window->decStrong((void*)ANativeWindow_acquire);
+}
diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp
index 4407af6..09232b6 100644
--- a/libs/hostgraphics/Android.bp
+++ b/libs/hostgraphics/Android.bp
@@ -17,26 +17,18 @@
static_libs: [
"libbase",
"libmath",
+ "libui-types",
"libutils",
],
srcs: [
- ":libui_host_common",
"ADisplay.cpp",
+ "ANativeWindow.cpp",
"Fence.cpp",
"HostBufferQueue.cpp",
"PublicFormat.cpp",
],
- include_dirs: [
- // Here we override all the headers automatically included with frameworks/native/include.
- // When frameworks/native/include will be removed from the list of automatic includes.
- // We will have to copy necessary headers with a pre-build step (generated headers).
- ".",
- "frameworks/native/libs/arect/include",
- "frameworks/native/libs/ui/include_private",
- ],
-
header_libs: [
"libnativebase_headers",
"libnativedisplay_headers",
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 29bb1b9..7439fbc 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -96,6 +96,7 @@
],
cflags: [
"-Wno-unused-variable",
+ "-D__INTRODUCED_IN(n)=",
],
},
},
@@ -538,7 +539,11 @@
"pipeline/skia/RenderNodeDrawable.cpp",
"pipeline/skia/ReorderBarrierDrawables.cpp",
"pipeline/skia/TransformCanvas.cpp",
+ "renderstate/RenderState.cpp",
+ "renderthread/CanvasContext.cpp",
+ "renderthread/DrawFrameTask.cpp",
"renderthread/Frame.cpp",
+ "renderthread/RenderProxy.cpp",
"renderthread/RenderTask.cpp",
"renderthread/TimeLord.cpp",
"hwui/AnimatedImageDrawable.cpp",
@@ -588,6 +593,7 @@
"SkiaCanvas.cpp",
"SkiaInterpolator.cpp",
"Tonemapper.cpp",
+ "TreeInfo.cpp",
"VectorDrawable.cpp",
],
@@ -615,16 +621,12 @@
"pipeline/skia/SkiaVulkanPipeline.cpp",
"pipeline/skia/VkFunctorDrawable.cpp",
"pipeline/skia/VkInteropFunctorDrawable.cpp",
- "renderstate/RenderState.cpp",
"renderthread/CacheManager.cpp",
- "renderthread/CanvasContext.cpp",
- "renderthread/DrawFrameTask.cpp",
"renderthread/EglManager.cpp",
"renderthread/ReliableSurface.cpp",
"renderthread/RenderEffectCapabilityQuery.cpp",
"renderthread/VulkanManager.cpp",
"renderthread/VulkanSurface.cpp",
- "renderthread/RenderProxy.cpp",
"renderthread/RenderThread.cpp",
"renderthread/HintSessionWrapper.cpp",
"service/GraphicsStatsService.cpp",
@@ -636,7 +638,6 @@
"Layer.cpp",
"ProfileDataContainer.cpp",
"Readback.cpp",
- "TreeInfo.cpp",
"WebViewFunctorManager.cpp",
"protos/graphicsstats.proto",
],
@@ -654,6 +655,8 @@
srcs: [
"platform/host/renderthread/CacheManager.cpp",
+ "platform/host/renderthread/HintSessionWrapper.cpp",
+ "platform/host/renderthread/ReliableSurface.cpp",
"platform/host/renderthread/RenderThread.cpp",
"platform/host/ProfileDataContainer.cpp",
"platform/host/Readback.cpp",
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index f526a28..589abb4 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -16,18 +16,6 @@
#include "RenderNode.h"
-#include "DamageAccumulator.h"
-#include "Debug.h"
-#include "Properties.h"
-#include "TreeInfo.h"
-#include "VectorDrawable.h"
-#include "private/hwui/WebViewFunctor.h"
-#ifdef __ANDROID__
-#include "renderthread/CanvasContext.h"
-#else
-#include "DamageAccumulator.h"
-#include "pipeline/skia/SkiaDisplayList.h"
-#endif
#include <SkPathOps.h>
#include <gui/TraceUtils.h>
#include <ui/FatVector.h>
@@ -37,6 +25,14 @@
#include <sstream>
#include <string>
+#include "DamageAccumulator.h"
+#include "Debug.h"
+#include "Properties.h"
+#include "TreeInfo.h"
+#include "VectorDrawable.h"
+#include "private/hwui/WebViewFunctor.h"
+#include "renderthread/CanvasContext.h"
+
#ifdef __ANDROID__
#include "include/gpu/ganesh/SkImageGanesh.h"
#endif
@@ -186,7 +182,6 @@
}
void RenderNode::pushLayerUpdate(TreeInfo& info) {
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext and Layers
LayerType layerType = properties().effectiveLayerType();
// If we are not a layer OR we cannot be rendered (eg, view was detached)
// we need to destroy any Layers we may have had previously
@@ -218,7 +213,6 @@
// That might be us, so tell CanvasContext that this layer is in the
// tree and should not be destroyed.
info.canvasContext.markLayerInUse(this);
-#endif
}
/**
diff --git a/libs/hwui/RootRenderNode.cpp b/libs/hwui/RootRenderNode.cpp
index ddbbf58..5174e27 100644
--- a/libs/hwui/RootRenderNode.cpp
+++ b/libs/hwui/RootRenderNode.cpp
@@ -18,11 +18,12 @@
#ifdef __ANDROID__ // Layoutlib does not support Looper (windows)
#include <utils/Looper.h>
+#else
+#include "utils/MessageHandler.h"
#endif
namespace android::uirenderer {
-#ifdef __ANDROID__ // Layoutlib does not support Looper
class FinishAndInvokeListener : public MessageHandler {
public:
explicit FinishAndInvokeListener(PropertyValuesAnimatorSet* anim) : mAnimator(anim) {
@@ -237,9 +238,13 @@
// user events, in which case the already posted listener's id will become stale, and
// the onFinished callback will then be ignored.
sp<FinishAndInvokeListener> message = new FinishAndInvokeListener(anim);
+#ifdef __ANDROID__ // Layoutlib does not support Looper
auto looper = Looper::getForThread();
LOG_ALWAYS_FATAL_IF(looper == nullptr, "Not on a looper thread?");
looper->sendMessageDelayed(ms2ns(remainingTimeInMs), message, 0);
+#else
+ message->handleMessage(0);
+#endif
anim->clearOneShotListener();
}
}
@@ -285,22 +290,5 @@
AnimationContext* ContextFactoryImpl::createAnimationContext(renderthread::TimeLord& clock) {
return new AnimationContextBridge(clock, mRootNode);
}
-#else
-
-void RootRenderNode::prepareTree(TreeInfo& info) {
- info.errorHandler = mErrorHandler.get();
- info.updateWindowPositions = true;
- RenderNode::prepareTree(info);
- info.updateWindowPositions = false;
- info.errorHandler = nullptr;
-}
-
-void RootRenderNode::attachAnimatingNode(RenderNode* animatingNode) { }
-
-void RootRenderNode::destroy() { }
-
-void RootRenderNode::addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { }
-
-#endif
} // namespace android::uirenderer
diff --git a/libs/hwui/RootRenderNode.h b/libs/hwui/RootRenderNode.h
index 1d3f5a8..7a5cda7 100644
--- a/libs/hwui/RootRenderNode.h
+++ b/libs/hwui/RootRenderNode.h
@@ -74,7 +74,6 @@
void detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim);
};
-#ifdef __ANDROID__ // Layoutlib does not support Animations
class ContextFactoryImpl : public IContextFactory {
public:
explicit ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {}
@@ -84,6 +83,5 @@
private:
RootRenderNode* mRootNode;
};
-#endif
} // namespace android::uirenderer
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index efa9b11..9d16ee8 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -87,7 +87,7 @@
WebViewFunctorManager::instance().releaseFunctor(functor);
}
-void WebViewFunctor_reportRenderingThreads(int functor, const int32_t* thread_ids, size_t size) {
+void WebViewFunctor_reportRenderingThreads(int functor, const pid_t* thread_ids, size_t size) {
WebViewFunctorManager::instance().reportRenderingThreads(functor, thread_ids, size);
}
@@ -265,8 +265,8 @@
funcs.transactionDeleteFunc(transaction);
}
-void WebViewFunctor::reportRenderingThreads(const int32_t* thread_ids, size_t size) {
- mRenderingThreads = std::vector<int32_t>(thread_ids, thread_ids + size);
+void WebViewFunctor::reportRenderingThreads(const pid_t* thread_ids, size_t size) {
+ mRenderingThreads = std::vector<pid_t>(thread_ids, thread_ids + size);
}
WebViewFunctorManager& WebViewFunctorManager::instance() {
@@ -355,7 +355,7 @@
}
}
-void WebViewFunctorManager::reportRenderingThreads(int functor, const int32_t* thread_ids,
+void WebViewFunctorManager::reportRenderingThreads(int functor, const pid_t* thread_ids,
size_t size) {
std::lock_guard _lock{mLock};
for (auto& iter : mFunctors) {
@@ -366,8 +366,8 @@
}
}
-std::vector<int32_t> WebViewFunctorManager::getRenderingThreadsForActiveFunctors() {
- std::vector<int32_t> renderingThreads;
+std::vector<pid_t> WebViewFunctorManager::getRenderingThreadsForActiveFunctors() {
+ std::vector<pid_t> renderingThreads;
std::lock_guard _lock{mLock};
for (const auto& iter : mActiveFunctors) {
const auto& functorThreads = iter->getRenderingThreads();
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index 2d77dd8..ec17640 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -17,13 +17,11 @@
#pragma once
#include <private/hwui/WebViewFunctor.h>
-#ifdef __ANDROID__ // Layoutlib does not support render thread
#include <renderthread/RenderProxy.h>
-#endif
-
#include <utils/LightRefBase.h>
#include <utils/Log.h>
#include <utils/StrongPointer.h>
+
#include <mutex>
#include <vector>
@@ -38,11 +36,7 @@
class Handle : public LightRefBase<Handle> {
public:
- ~Handle() {
-#ifdef __ANDROID__ // Layoutlib does not support render thread
- renderthread::RenderProxy::destroyFunctor(id());
-#endif
- }
+ ~Handle() { renderthread::RenderProxy::destroyFunctor(id()); }
int id() const { return mReference.id(); }
@@ -60,7 +54,7 @@
void onRemovedFromTree() { mReference.onRemovedFromTree(); }
- const std::vector<int32_t>& getRenderingThreads() const {
+ const std::vector<pid_t>& getRenderingThreads() const {
return mReference.getRenderingThreads();
}
@@ -85,8 +79,8 @@
ASurfaceControl* getSurfaceControl();
void mergeTransaction(ASurfaceTransaction* transaction);
- void reportRenderingThreads(const int32_t* thread_ids, size_t size);
- const std::vector<int32_t>& getRenderingThreads() const { return mRenderingThreads; }
+ void reportRenderingThreads(const pid_t* thread_ids, size_t size);
+ const std::vector<pid_t>& getRenderingThreads() const { return mRenderingThreads; }
sp<Handle> createHandle() {
LOG_ALWAYS_FATAL_IF(mCreatedHandle);
@@ -107,7 +101,7 @@
bool mCreatedHandle = false;
int32_t mParentSurfaceControlGenerationId = 0;
ASurfaceControl* mSurfaceControl = nullptr;
- std::vector<int32_t> mRenderingThreads;
+ std::vector<pid_t> mRenderingThreads;
};
class WebViewFunctorManager {
@@ -118,8 +112,8 @@
void releaseFunctor(int functor);
void onContextDestroyed();
void destroyFunctor(int functor);
- void reportRenderingThreads(int functor, const int32_t* thread_ids, size_t size);
- std::vector<int32_t> getRenderingThreadsForActiveFunctors();
+ void reportRenderingThreads(int functor, const pid_t* thread_ids, size_t size);
+ std::vector<pid_t> getRenderingThreadsForActiveFunctors();
sp<WebViewFunctor::Handle> handleFor(int functor);
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index 770822a..fd9915a 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -164,8 +164,10 @@
} // namespace android
using namespace android;
+using namespace android::uirenderer;
void init_android_graphics() {
+ Properties::overrideRenderPipelineType(RenderPipelineType::SkiaCpu);
SkGraphics::Init();
}
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index 0f80c55..b01e38d 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -27,6 +27,8 @@
#include <hwui/ImageDecoder.h>
#ifdef __ANDROID__
#include <utils/Looper.h>
+#else
+#include "utils/MessageHandler.h"
#endif
#include "ColorFilter.h"
@@ -182,23 +184,6 @@
drawable->setRepetitionCount(loopCount);
}
-#ifndef __ANDROID__
-struct Message {
- Message(int w) {}
-};
-
-class MessageHandler : public virtual RefBase {
-protected:
- virtual ~MessageHandler() override {}
-
-public:
- /**
- * Handles a message.
- */
- virtual void handleMessage(const Message& message) = 0;
-};
-#endif
-
class InvokeListener : public MessageHandler {
public:
InvokeListener(JNIEnv* env, jobject javaObject) {
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 9e21f86..d415700 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -1,8 +1,14 @@
// #define LOG_NDEBUG 0
#include "Bitmap.h"
+#include <android-base/unique_fd.h>
#include <hwui/Bitmap.h>
#include <hwui/Paint.h>
+#include <inttypes.h>
+#include <renderthread/RenderProxy.h>
+#include <string.h>
+
+#include <memory>
#include "CreateJavaOutputStreamAdaptor.h"
#include "Gainmap.h"
@@ -24,16 +30,6 @@
#include "SkTypes.h"
#include "android_nio_utils.h"
-#ifdef __ANDROID__ // Layoutlib does not support graphic buffer, parcel or render thread
-#include <android-base/unique_fd.h>
-#include <renderthread/RenderProxy.h>
-#endif
-
-#include <inttypes.h>
-#include <string.h>
-
-#include <memory>
-
#define DEBUG_PARCEL 0
static jclass gBitmap_class;
@@ -1105,11 +1101,9 @@
}
static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapPtr) {
-#ifdef __ANDROID__ // Layoutlib does not support render thread
LocalScopedBitmap bitmapHandle(bitmapPtr);
if (!bitmapHandle.valid()) return;
android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmapHandle->bitmap());
-#endif
}
static jint Bitmap_getAllocationByteCount(JNIEnv* env, jobject, jlong bitmapPtr) {
diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
index 426644e..948362c 100644
--- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
+++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
@@ -16,22 +16,19 @@
#include "GraphicsJNI.h"
-#ifdef __ANDROID__ // Layoutlib does not support Looper and device properties
+#ifdef __ANDROID__ // Layoutlib does not support Looper
#include <utils/Looper.h>
#endif
-#include <SkRegion.h>
-#include <SkRuntimeEffect.h>
-
+#include <CanvasProperty.h>
#include <Rect.h>
#include <RenderNode.h>
-#include <CanvasProperty.h>
+#include <SkRegion.h>
+#include <SkRuntimeEffect.h>
#include <hwui/Canvas.h>
#include <hwui/Paint.h>
#include <minikin/Layout.h>
-#ifdef __ANDROID__ // Layoutlib does not support RenderThread
#include <renderthread/RenderProxy.h>
-#endif
namespace android {
@@ -85,11 +82,7 @@
}
static jint android_view_DisplayListCanvas_getMaxTextureSize(JNIEnv*, jobject) {
-#ifdef __ANDROID__ // Layoutlib does not support RenderProxy (RenderThread)
return android::uirenderer::renderthread::RenderProxy::maxTextureSize();
-#else
- return 4096;
-#endif
}
static void android_view_DisplayListCanvas_enableZ(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index a7d6423..6e03bbd 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -15,19 +15,17 @@
*/
#define ATRACE_TAG ATRACE_TAG_VIEW
-#include "GraphicsJNI.h"
-
#include <Animator.h>
#include <DamageAccumulator.h>
#include <Matrix.h>
#include <RenderNode.h>
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
-#include <renderthread/CanvasContext.h>
-#endif
#include <TreeInfo.h>
#include <effects/StretchEffect.h>
#include <gui/TraceUtils.h>
#include <hwui/Paint.h>
+#include <renderthread/CanvasContext.h>
+
+#include "GraphicsJNI.h"
namespace android {
@@ -640,7 +638,6 @@
ATRACE_NAME("Update SurfaceView position");
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
JNIEnv* env = jnienv();
// Update the new position synchronously. We cannot defer this to
// a worker pool to process asynchronously because the UI thread
@@ -669,7 +666,6 @@
env->DeleteGlobalRef(mListener);
mListener = nullptr;
}
-#endif
}
virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override {
@@ -682,7 +678,6 @@
ATRACE_NAME("SurfaceView position lost");
JNIEnv* env = jnienv();
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
// Update the lost position synchronously. We cannot defer this to
// a worker pool to process asynchronously because the UI thread
// may be unblocked by the time a worker thread can process this,
@@ -698,7 +693,6 @@
env->DeleteGlobalRef(mListener);
mListener = nullptr;
}
-#endif
}
private:
@@ -750,7 +744,6 @@
StretchEffectBehavior::Shader) {
JNIEnv* env = jnienv();
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
SkVector stretchDirection = effect->getStretchDirection();
jboolean keepListening = env->CallStaticBooleanMethod(
gPositionListener.clazz, gPositionListener.callApplyStretch, mListener,
@@ -762,7 +755,6 @@
env->DeleteGlobalRef(mListener);
mListener = nullptr;
}
-#endif
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index e0216b6..36dc933 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -15,23 +15,19 @@
*/
#include "SkiaDisplayList.h"
-#include "FunctorDrawable.h"
-
-#include "DumpOpsCanvas.h"
-#ifdef __ANDROID__ // Layoutlib does not support SkiaPipeline
-#include "SkiaPipeline.h"
-#else
-#include "DamageAccumulator.h"
-#endif
-#include "TreeInfo.h"
-#include "VectorDrawable.h"
-#ifdef __ANDROID__
-#include "renderthread/CanvasContext.h"
-#endif
#include <SkImagePriv.h>
#include <SkPathOps.h>
+// clang-format off
+#include "FunctorDrawable.h" // Must be included before DumpOpsCanvas.h
+#include "DumpOpsCanvas.h"
+// clang-format on
+#include "SkiaPipeline.h"
+#include "TreeInfo.h"
+#include "VectorDrawable.h"
+#include "renderthread/CanvasContext.h"
+
namespace android {
namespace uirenderer {
namespace skiapipeline {
@@ -101,7 +97,6 @@
// If the prepare tree is triggered by the UI thread and no previous call to
// pinImages has failed then we must pin all mutable images in the GPU cache
// until the next UI thread draw.
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
if (info.prepareTextures && !info.canvasContext.pinImages(mMutableImages)) {
// In the event that pinning failed we prevent future pinImage calls for the
// remainder of this tree traversal and also unpin any currently pinned images
@@ -110,11 +105,11 @@
info.canvasContext.unpinImages();
}
+#ifdef __ANDROID__
auto grContext = info.canvasContext.getGrContext();
for (const auto& bufferData : mMeshBufferData) {
bufferData->updateBuffers(grContext);
}
-
#endif
bool hasBackwardProjectedNodesHere = false;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 34932b1..dc669a5 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -24,7 +24,6 @@
#include <SkImageAndroid.h>
#include <SkImageInfo.h>
#include <SkMatrix.h>
-#include <SkMultiPictureDocument.h>
#include <SkOverdrawCanvas.h>
#include <SkOverdrawColorFilter.h>
#include <SkPicture.h>
@@ -38,6 +37,7 @@
#include <android-base/properties.h>
#include <gui/TraceUtils.h>
#include <include/android/SkSurfaceAndroid.h>
+#include <include/docs/SkMultiPictureDocument.h>
#include <include/encode/SkPngEncoder.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <unistd.h>
@@ -185,7 +185,7 @@
// we need to keep it until after mMultiPic.close()
// procs is passed as a pointer, but just as a method of having an optional default.
// procs doesn't need to outlive this Make call.
- mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs,
+ mMultiPic = SkMultiPictureDocument::Make(mOpenMultiPicStream.get(), &procs,
[sharingCtx = mSerialContext.get()](const SkPicture* pic) {
SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
});
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index cf14b1f..823b209 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -18,7 +18,6 @@
#include <SkColorSpace.h>
#include <SkDocument.h>
-#include <SkMultiPictureDocument.h>
#include <SkSurface.h>
#include "Lighting.h"
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index b62711f..21fe6ff 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -16,10 +16,10 @@
#include "VkFunctorDrawable.h"
-#include <GrBackendDrawableInfo.h>
#include <SkAndroidFrameworkUtils.h>
#include <SkImage.h>
#include <SkM44.h>
+#include <include/gpu/ganesh/vk/GrBackendDrawableInfo.h>
#include <gui/TraceUtils.h>
#include <private/hwui/DrawVkInfo.h>
#include <utils/Color.h>
diff --git a/libs/hwui/platform/host/WebViewFunctorManager.cpp b/libs/hwui/platform/host/WebViewFunctorManager.cpp
index 1d16655..4ba206b 100644
--- a/libs/hwui/platform/host/WebViewFunctorManager.cpp
+++ b/libs/hwui/platform/host/WebViewFunctorManager.cpp
@@ -50,6 +50,8 @@
void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {}
+void WebViewFunctor::reportRenderingThreads(const pid_t* thread_ids, size_t size) {}
+
void WebViewFunctor::reparentSurfaceControl(ASurfaceControl* parent) {}
WebViewFunctorManager& WebViewFunctorManager::instance() {
@@ -68,6 +70,13 @@
void WebViewFunctorManager::destroyFunctor(int functor) {}
+void WebViewFunctorManager::reportRenderingThreads(int functor, const pid_t* thread_ids,
+ size_t size) {}
+
+std::vector<pid_t> WebViewFunctorManager::getRenderingThreadsForActiveFunctors() {
+ return {};
+}
+
sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) {
return nullptr;
}
diff --git a/libs/hwui/platform/host/renderthread/HintSessionWrapper.cpp b/libs/hwui/platform/host/renderthread/HintSessionWrapper.cpp
new file mode 100644
index 0000000..b1b1d58
--- /dev/null
+++ b/libs/hwui/platform/host/renderthread/HintSessionWrapper.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#include "renderthread/HintSessionWrapper.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+void HintSessionWrapper::HintSessionBinding::init() {}
+
+HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
+ : mUiThreadId(uiThreadId)
+ , mRenderThreadId(renderThreadId)
+ , mBinding(std::make_shared<HintSessionBinding>()) {}
+
+HintSessionWrapper::~HintSessionWrapper() {}
+
+void HintSessionWrapper::destroy() {}
+
+bool HintSessionWrapper::init() {
+ return false;
+}
+
+void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) {}
+
+void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {}
+
+void HintSessionWrapper::sendLoadResetHint() {}
+
+void HintSessionWrapper::sendLoadIncreaseHint() {}
+
+bool HintSessionWrapper::alive() {
+ return false;
+}
+
+nsecs_t HintSessionWrapper::getLastUpdate() {
+ return -1;
+}
+
+void HintSessionWrapper::delayedDestroy(RenderThread& rt, nsecs_t delay,
+ std::shared_ptr<HintSessionWrapper> wrapperPtr) {}
+
+void HintSessionWrapper::setActiveFunctorThreads(std::vector<pid_t> threadIds) {}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/renderthread/ReliableSurface.cpp b/libs/hwui/platform/host/renderthread/ReliableSurface.cpp
new file mode 100644
index 0000000..2deaaf3
--- /dev/null
+++ b/libs/hwui/platform/host/renderthread/ReliableSurface.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "renderthread/ReliableSurface.h"
+
+#include <log/log_main.h>
+#include <system/window.h>
+
+namespace android::uirenderer::renderthread {
+
+ReliableSurface::ReliableSurface(ANativeWindow* window) : mWindow(window) {
+ LOG_ALWAYS_FATAL_IF(!mWindow, "Error, unable to wrap a nullptr");
+ ANativeWindow_acquire(mWindow);
+}
+
+ReliableSurface::~ReliableSurface() {
+ ANativeWindow_release(mWindow);
+}
+
+void ReliableSurface::init() {}
+
+int ReliableSurface::reserveNext() {
+ return OK;
+}
+
+}; // namespace android::uirenderer::renderthread
diff --git a/libs/hwui/platform/host/renderthread/RenderThread.cpp b/libs/hwui/platform/host/renderthread/RenderThread.cpp
index 6f08b59..f9d0f47 100644
--- a/libs/hwui/platform/host/renderthread/RenderThread.cpp
+++ b/libs/hwui/platform/host/renderthread/RenderThread.cpp
@@ -17,6 +17,7 @@
#include "renderthread/RenderThread.h"
#include "Readback.h"
+#include "renderstate/RenderState.h"
#include "renderthread/VulkanManager.h"
namespace android {
@@ -66,6 +67,7 @@
RenderThread::~RenderThread() {}
void RenderThread::initThreadLocals() {
+ mRenderState = new RenderState(*this);
mCacheManager = new CacheManager(*this);
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/libs/hwui/platform/host/utils/MessageHandler.h
similarity index 66%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to libs/hwui/platform/host/utils/MessageHandler.h
index f6140f5..51ee48e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/libs/hwui/platform/host/utils/MessageHandler.h
@@ -14,9 +14,21 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+#pragma once
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+#include <utils/RefBase.h>
+
+struct Message {
+ Message(int w) {}
+};
+
+class MessageHandler : public virtual android::RefBase {
+protected:
+ virtual ~MessageHandler() override {}
+
+public:
+ /**
+ * Handles a message.
+ */
+ virtual void handleMessage(const Message& message) = 0;
+};
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index e08d32a..60657cf 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -16,11 +16,13 @@
#ifndef RENDERSTATE_H
#define RENDERSTATE_H
-#include "utils/Macros.h"
-
+#include <pthread.h>
#include <utils/RefBase.h>
+
#include <set>
+#include "utils/Macros.h"
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 22de2f2..66e0896 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -35,6 +35,7 @@
#include "Properties.h"
#include "RenderThread.h"
#include "hwui/Canvas.h"
+#include "pipeline/skia/SkiaCpuPipeline.h"
#include "pipeline/skia/SkiaGpuPipeline.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
@@ -72,7 +73,7 @@
CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
RenderNode* rootRenderNode, IContextFactory* contextFactory,
- int32_t uiThreadId, int32_t renderThreadId) {
+ pid_t uiThreadId, pid_t renderThreadId) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
@@ -84,6 +85,12 @@
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread),
uiThreadId, renderThreadId);
+#ifndef __ANDROID__
+ case RenderPipelineType::SkiaCpu:
+ return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
+ std::make_unique<skiapipeline::SkiaCpuPipeline>(thread),
+ uiThreadId, renderThreadId);
+#endif
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
break;
@@ -182,6 +189,7 @@
}
void CanvasContext::setHardwareBuffer(AHardwareBuffer* buffer) {
+#ifdef __ANDROID__
if (mHardwareBuffer) {
AHardwareBuffer_release(mHardwareBuffer);
mHardwareBuffer = nullptr;
@@ -192,6 +200,7 @@
mHardwareBuffer = buffer;
}
mRenderPipeline->setHardwareBuffer(mHardwareBuffer);
+#endif
}
void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
@@ -561,6 +570,7 @@
}
void CanvasContext::draw(bool solelyTextureViewUpdates) {
+#ifdef __ANDROID__
if (auto grContext = getGrContext()) {
if (grContext->abandoned()) {
if (grContext->isDeviceLost()) {
@@ -571,6 +581,7 @@
return;
}
}
+#endif
SkRect dirty;
mDamageAccumulator.finish(&dirty);
@@ -594,11 +605,13 @@
if (skippedFrameReason) {
mCurrentFrameInfo->setSkippedFrameReason(*skippedFrameReason);
+#ifdef __ANDROID__
if (auto grContext = getGrContext()) {
// Submit to ensure that any texture uploads complete and Skia can
// free its staging buffers.
grContext->flushAndSubmit();
}
+#endif
// Notify the callbacks, even if there's nothing to draw so they aren't waiting
// indefinitely
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 1b333bf..826d00e 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -140,12 +140,14 @@
if (CC_LIKELY(canDrawThisFrame)) {
context->draw(solelyTextureViewUpdates);
} else {
+#ifdef __ANDROID__
// Do a flush in case syncFrameState performed any texture uploads. Since we skipped
// the draw() call, those uploads (or deletes) will end up sitting in the queue.
// Do them now
if (GrDirectContext* grContext = mRenderThread->getGrContext()) {
grContext->flushAndSubmit();
}
+#endif
// wait on fences so tasks don't overlap next frame
context->waitOnFences();
}
@@ -176,11 +178,13 @@
bool canDraw = mContext->makeCurrent();
mContext->unpinImages();
+#ifdef __ANDROID__
for (size_t i = 0; i < mLayers.size(); i++) {
if (mLayers[i]) {
mLayers[i]->apply();
}
}
+#endif
mLayers.clear();
mContext->setContentDrawBounds(mContentDrawBounds);
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index 5959647..d6a4d50 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -21,7 +21,9 @@
#include <apex/window.h>
#include <utils/Errors.h>
#include <utils/Macros.h>
+#ifdef __ANDROID__
#include <utils/NdkUtils.h>
+#endif
#include <utils/StrongPointer.h>
#include <memory>
@@ -62,9 +64,11 @@
mutable std::mutex mMutex;
uint64_t mUsage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
+#ifdef __ANDROID__
AHardwareBuffer_Format mFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
UniqueAHardwareBuffer mScratchBuffer;
ANativeWindowBuffer* mReservedBuffer = nullptr;
+#endif
base::unique_fd mReservedFenceFd;
bool mHasDequeuedBuffer = false;
int mBufferQueueState = OK;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index eab3605..715153b 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -42,7 +42,11 @@
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory)
: mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
+#ifdef __ANDROID__
pid_t uiThreadId = pthread_gettid_np(pthread_self());
+#else
+ pid_t uiThreadId = 0;
+#endif
pid_t renderThreadId = getRenderThreadTid();
mContext = mRenderThread.queue().runSync([=, this]() -> CanvasContext* {
CanvasContext* context = CanvasContext::create(mRenderThread, translucent, rootRenderNode,
@@ -90,6 +94,7 @@
}
void RenderProxy::setHardwareBuffer(AHardwareBuffer* buffer) {
+#ifdef __ANDROID__
if (buffer) {
AHardwareBuffer_acquire(buffer);
}
@@ -99,6 +104,7 @@
AHardwareBuffer_release(hardwareBuffer);
}
});
+#endif
}
void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
@@ -216,7 +222,9 @@
}
void RenderProxy::detachSurfaceTexture(DeferredLayerUpdater* layer) {
+#ifdef __ANDROID__
return mRenderThread.queue().runSync([&]() { layer->detachSurfaceTexture(); });
+#endif
}
void RenderProxy::destroyHardwareResources() {
@@ -324,11 +332,13 @@
}
});
}
+#ifdef __ANDROID__
if (!Properties::isolatedProcess) {
std::string grallocInfo;
GraphicBufferAllocator::getInstance().dump(grallocInfo);
dprintf(fd, "%s\n", grallocInfo.c_str());
}
+#endif
}
void RenderProxy::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
@@ -352,7 +362,11 @@
}
int RenderProxy::getRenderThreadTid() {
+#ifdef __ANDROID__
return mRenderThread.getTid();
+#else
+ return 0;
+#endif
}
void RenderProxy::addRenderNode(RenderNode* node, bool placeFront) {
@@ -461,7 +475,7 @@
int RenderProxy::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
ATRACE_NAME("HardwareBitmap readback");
RenderThread& thread = RenderThread::getInstance();
- if (gettid() == thread.getTid()) {
+ if (RenderThread::isCurrent()) {
// TODO: fix everything that hits this. We should never be triggering a readback ourselves.
return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap);
} else {
@@ -472,7 +486,7 @@
int RenderProxy::copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap) {
RenderThread& thread = RenderThread::getInstance();
- if (gettid() == thread.getTid()) {
+ if (RenderThread::isCurrent()) {
// TODO: fix everything that hits this. We should never be triggering a readback ourselves.
return (int)thread.readback().copyImageInto(image, bitmap);
} else {
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 5aa006b..c664d3d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -2713,7 +2713,7 @@
List<RoutingSessionInfo> sessionInfos = getRoutingSessions();
RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1);
- transfer(targetSession, route, Process.myUserHandle(), mContext.getPackageName());
+ transfer(targetSession, route, mClientUser, mContext.getPackageName());
}
@Override
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index 2a0648d..7ed67dc 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -23,6 +23,9 @@
import android.annotation.TestApi;
import android.app.Activity;
import android.app.ActivityOptions.LaunchCookie;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.compat.annotation.Overridable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -66,6 +69,18 @@
private static final String TAG = "MediaProjectionManager";
/**
+ * This change id ensures that users are presented with a choice of capturing a single app
+ * or the entire screen when initiating a MediaProjection session, overriding the usage of
+ * MediaProjectionConfig#createConfigForDefaultDisplay.
+ *
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ public static final long OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION = 316897322L;
+
+ /**
* Intent extra to customize the permission dialog based on the host app's preferences.
* @hide
*/
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 207ccbe..871e9ab 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -80,4 +80,7 @@
boolean hasCustomMediaSessionPolicyProvider(String componentName);
int getSessionPolicies(in MediaSession.Token token);
void setSessionPolicies(in MediaSession.Token token, int policies);
+
+ // For testing of temporarily engaged sessions.
+ void expireTempEngagedSessions();
}
diff --git a/media/java/android/media/session/ParcelableListBinder.java b/media/java/android/media/session/ParcelableListBinder.java
index bbf1e08..d788284 100644
--- a/media/java/android/media/session/ParcelableListBinder.java
+++ b/media/java/android/media/session/ParcelableListBinder.java
@@ -45,6 +45,7 @@
private static final int END_OF_PARCEL = 0;
private static final int ITEM_CONTINUED = 1;
+ private final Class<T> mListElementsClass;
private final Consumer<List<T>> mConsumer;
private final Object mLock = new Object();
@@ -61,9 +62,11 @@
/**
* Creates an instance.
*
+ * @param listElementsClass the class of the list elements.
* @param consumer a consumer that consumes the list received
*/
- public ParcelableListBinder(@NonNull Consumer<List<T>> consumer) {
+ public ParcelableListBinder(Class<T> listElementsClass, @NonNull Consumer<List<T>> consumer) {
+ mListElementsClass = listElementsClass;
mConsumer = consumer;
}
@@ -83,7 +86,13 @@
mCount = data.readInt();
}
while (i < mCount && data.readInt() != END_OF_PARCEL) {
- mList.add(data.readParcelable(null));
+ Object object = data.readParcelable(null);
+ if (mListElementsClass.isAssignableFrom(object.getClass())) {
+ // Checking list items are of compaitible types to validate against malicious
+ // apps calling it directly via reflection with non compilable items.
+ // See b/317048338 for more details
+ mList.add((T) object);
+ }
i++;
}
if (i >= mCount) {
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 82561f9..4f9917b 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2886,6 +2886,10 @@
jint offset,
jint size,
sp<hardware::HidlMemory> *memory) {
+ if ((offset + size) > context->capacity()) {
+ ALOGW("extractMemoryFromContext: offset + size provided exceed capacity");
+ return;
+ }
*memory = context->toHidlMemory();
if (*memory == nullptr) {
if (!context->mBlock) {
@@ -2893,23 +2897,26 @@
return;
}
ALOGD("extractMemoryFromContext: realloc & copying from C2Block to IMemory (cap=%zu)",
- context->capacity());
+ context->capacity());
if (!obtain(context, context->capacity(),
context->mCodecNames, true /* secure */)) {
ALOGW("extractMemoryFromContext: failed to obtain secure block");
return;
}
- C2WriteView view = context->mBlock->map().get();
- if (view.error() != C2_OK) {
- ALOGW("extractMemoryFromContext: failed to map C2Block (%d)", view.error());
- return;
- }
- uint8_t *memoryPtr = static_cast<uint8_t *>(context->mMemory->unsecurePointer());
- memcpy(memoryPtr + offset, view.base() + offset, size);
- context->mBlock.reset();
- context->mReadWriteMapping.reset();
*memory = context->toHidlMemory();
}
+ if (context->mBlock == nullptr || context->mReadWriteMapping == nullptr) {
+ ALOGW("extractMemoryFromContext: Cannot extract memory as C2Block is not created/mapped");
+ return;
+ }
+ if (context->mReadWriteMapping->error() != C2_OK) {
+ ALOGW("extractMemoryFromContext: failed to map C2Block (%d)",
+ context->mReadWriteMapping->error());
+ return;
+ }
+ // We are proceeding to extract memory from C2Block
+ uint8_t *memoryPtr = static_cast<uint8_t *>(context->mMemory->unsecurePointer());
+ memcpy(memoryPtr + offset, context->mReadWriteMapping->base() + offset, size);
}
static void extractBufferFromContext(
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index c836df3..520b76e 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -361,6 +361,7 @@
* Tests if MR2.SessionCallback.onSessionCreated is called
* when a route is selected from MR2Manager.
*/
+ @Ignore // Ignored due to flakiness. No plans to fix though, in favor of removal (b/334970551).
@Test
public void testRouterOnSessionCreated() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
@@ -512,6 +513,7 @@
/**
* Tests select, transfer, release of routes of a provider
*/
+ @Ignore // Ignored due to flakiness. No plans to fix though, in favor of removal (b/334970551).
@Test
public void testSelectAndTransferAndRelease() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
@@ -908,6 +910,7 @@
* Tests if getSelectableRoutes and getDeselectableRoutes filter routes based on
* selected routes
*/
+ @Ignore // Ignored due to flakiness. No plans to fix though, in favor of removal (b/334970551).
@Test
public void testGetSelectableRoutes_notReturnsSelectedRoutes() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
diff --git a/nfc/Android.bp b/nfc/Android.bp
index c186804..13ac231 100644
--- a/nfc/Android.bp
+++ b/nfc/Android.bp
@@ -66,6 +66,7 @@
],
impl_library_visibility: [
"//frameworks/base:__subpackages__",
+ "//cts/hostsidetests/multidevices/nfc:__subpackages__",
"//cts/tests/tests/nfc",
"//packages/apps/Nfc:__subpackages__",
],
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index b57d548..7cd7e7ab 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -35,6 +35,7 @@
import android.nfc.INfcWlcStateListener;
import android.nfc.NfcAntennaInfo;
import android.nfc.WlcListenerDeviceInfo;
+import android.nfc.cardemulation.PollingFrame;
import android.os.Bundle;
/**
@@ -101,7 +102,7 @@
void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags);
- void notifyPollingLoop(in Bundle frame);
+ void notifyPollingLoop(in PollingFrame frame);
void notifyHceDeactivated();
int sendVendorNciMessage(int mt, int gid, int oid, in byte[] payload);
void registerVendorExtensionCallback(in INfcVendorNciCallback callbacks);
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index b44a71b..29867d9 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -2803,12 +2803,11 @@
@TestApi
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
public void notifyPollingLoop(@NonNull PollingFrame pollingFrame) {
- Bundle frame = pollingFrame.toBundle();
try {
if (sService == null) {
attemptDeadServiceRecovery(null);
}
- sService.notifyPollingLoop(frame);
+ sService.notifyPollingLoop(pollingFrame);
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
// Try one more time
@@ -2817,7 +2816,7 @@
return;
}
try {
- sService.notifyPollingLoop(frame);
+ sService.notifyPollingLoop(pollingFrame);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover NFC Service.");
}
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index 61037a2..f674b06a 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -325,15 +325,12 @@
}
break;
case MSG_POLLING_LOOP:
- ArrayList<Bundle> frames =
- msg.getData().getParcelableArrayList(KEY_POLLING_LOOP_FRAMES_BUNDLE,
- Bundle.class);
- ArrayList<PollingFrame> pollingFrames =
- new ArrayList<PollingFrame>(frames.size());
- for (Bundle frame : frames) {
- pollingFrames.add(new PollingFrame(frame));
+ if (android.nfc.Flags.nfcReadPollingLoop()) {
+ ArrayList<PollingFrame> pollingFrames =
+ msg.getData().getParcelableArrayList(
+ KEY_POLLING_LOOP_FRAMES_BUNDLE, PollingFrame.class);
+ processPollingFrames(pollingFrames);
}
- processPollingFrames(pollingFrames);
break;
default:
super.handleMessage(msg);
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/nfc/java/android/nfc/cardemulation/PollingFrame.aidl
similarity index 86%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to nfc/java/android/nfc/cardemulation/PollingFrame.aidl
index f6140f5..8e09f8b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/nfc/java/android/nfc/cardemulation/PollingFrame.aidl
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package android.nfc.cardemulation;
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+parcelable PollingFrame;
\ No newline at end of file
diff --git a/nfc/java/android/nfc/cardemulation/PollingFrame.java b/nfc/java/android/nfc/cardemulation/PollingFrame.java
index c6861bf..b52faba 100644
--- a/nfc/java/android/nfc/cardemulation/PollingFrame.java
+++ b/nfc/java/android/nfc/cardemulation/PollingFrame.java
@@ -101,47 +101,37 @@
/**
* KEY_POLLING_LOOP_TYPE is the Bundle key for the type of
* polling loop frame in the Bundle included in MSG_POLLING_LOOP.
- *
- * @hide
*/
@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
+ private static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
/**
* KEY_POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
* the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
- *
- * @hide
*/
@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
+ private static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
/**
* KEY_POLLING_LOOP_GAIN is the Bundle key for the field strength of
* the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
- *
- * @hide
- */
+ */
@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
+ private static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
/**
* KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for the timestamp of
* the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
- *
- * @hide
- */
+ */
@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
+ private static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
/**
* KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for whether this polling frame triggered
* autoTransact in the Bundle included in MSG_POLLING_LOOP.
- *
- * @hide
- */
+ */
@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT =
+ private static final String KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT =
"android.nfc.cardemulation.TRIGGERED_AUTOTRANSACT";
@@ -151,7 +141,7 @@
private final int mGain;
@DurationMillisLong
private final long mTimestamp;
- private final boolean mTriggeredAutoTransact;
+ private boolean mTriggeredAutoTransact;
public static final @NonNull Parcelable.Creator<PollingFrame> CREATOR =
new Parcelable.Creator<>() {
@@ -166,7 +156,7 @@
}
};
- PollingFrame(Bundle frame) {
+ private PollingFrame(Bundle frame) {
mType = frame.getInt(KEY_POLLING_LOOP_TYPE);
byte[] data = frame.getByteArray(KEY_POLLING_LOOP_DATA);
mData = (data == null) ? new byte[0] : data;
@@ -239,6 +229,13 @@
}
/**
+ * @hide
+ */
+ public void setTriggeredAutoTransact(boolean triggeredAutoTransact) {
+ mTriggeredAutoTransact = triggeredAutoTransact;
+ }
+
+ /**
* Returns whether this frame triggered the device to automatically disable observe mode and
* allow one transaction.
*/
@@ -257,11 +254,9 @@
}
/**
- *
- * @hide
* @return a Bundle representing this frame
*/
- public Bundle toBundle() {
+ private Bundle toBundle() {
Bundle frame = new Bundle();
frame.putInt(KEY_POLLING_LOOP_TYPE, getType());
if (getVendorSpecificGain() != -1) {
diff --git a/packages/BackupRestoreConfirmation/AndroidManifest.xml b/packages/BackupRestoreConfirmation/AndroidManifest.xml
index e67b3be..44aa1b1 100644
--- a/packages/BackupRestoreConfirmation/AndroidManifest.xml
+++ b/packages/BackupRestoreConfirmation/AndroidManifest.xml
@@ -26,7 +26,8 @@
android:allowBackup="false"
android:permission="android.permission.CONFIRM_FULL_BACKUP" >
- <activity android:name=".BackupRestoreConfirmation"
+ <activity android:name=".BackupRestoreConfirmation"
+ android:theme="@style/OptOutEdgeToEdgeEnforcement"
android:title=""
android:windowSoftInputMode="stateAlwaysHidden"
android:excludeFromRecents="true"
diff --git a/packages/BackupRestoreConfirmation/res/values/styles.xml b/packages/BackupRestoreConfirmation/res/values/styles.xml
new file mode 100644
index 0000000..ce54568
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/res/values/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <!--
+ TODO(b/309578419): Make activities handle insets properly and then remove this.
+ -->
+ <style name="OptOutEdgeToEdgeEnforcement">
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ </style>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index 183965e..d2df0e4 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -27,10 +27,10 @@
<string name="summary_glasses" msgid="2872254734959842579">"سيتم السماح لهذا التطبيق بالوصول إلى هذه الأذونات على \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
<string name="title_app_streaming" msgid="2270331024626446950">"السماح لتطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بالوصول إلى هذه المعلومات من هاتفك"</string>
<string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"هل تريد منح <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> إذنًا لبث التطبيقات المُثبَّتة على هاتفك؟"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"سيتمكّن \"%1$s\" من الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله على الهاتف، بما في ذلك الملفات الصوتية والصور وكلمات المرور والرسائل.<br/><br/>سيتمكّن \"%1$s\" من بث التطبيقات إلى أنّ توقف إمكانية استخدام هذا الإذن."</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"سيتمكّن %1$s من الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله على الهاتف، بما في ذلك الملفات الصوتية والصور وكلمات المرور والرسائل.<br/><br/>سيتمكّن %1$s من بث التطبيقات إلى أن توقف إمكانية استخدام هذا الإذن."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"الخدمات التي تعمل بين الأجهزة"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"يطلب تطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" لبثّ محتوى التطبيقات بين أجهزتك."</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"يطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" لعرض التطبيقات وبثها بين أجهزتك"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"تطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> لعرض التطبيقات وبثها بين أجهزتك"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"السماح لتطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بالوصول إلى هذه المعلومات من هاتفك"</string>
@@ -39,7 +39,7 @@
<string name="helper_summary_computer" msgid="8774832742608187072">"يطلب تطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" للوصول إلى الصور والوسائط والإشعارات في هاتفك."</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"هل تريد السماح للتطبيق <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> باتّخاذ هذا الإجراء؟"</string>
<string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"هل تريد منح <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> إذنًا لبث التطبيقات والوصول إلى ميزات النظام على هاتفك؟"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"سيتمكّن \"%1$s\" من الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله على هاتفك، بما في ذلك الملفات الصوتية والصور ومعلومات الدفع وكلمات المرور والرسائل.<br/><br/>سيتمكّن \"%1$s\" من بث التطبيقات والوصول إلى ميزات النظام إلى أنّ توقف إمكانية استخدام هذا الإذن."</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"سيتمكّن %1$s من الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله على هاتفك، بما في ذلك الملفات الصوتية والصور ومعلومات الدفع وكلمات المرور والرسائل.<br/><br/>سيتمكّن %1$s من بث التطبيقات والوصول إلى ميزات النظام إلى أن توقف إمكانية استخدام هذا الإذن."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"يطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" لبثّ التطبيقات وميزات النظام الأخرى إلى أجهزتك المجاورة."</string>
<string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string>
<string name="summary_generic" msgid="1761976003668044801">"سيتمكّن هذا التطبيق من مزامنة المعلومات، مثل اسم المتصل، بين هاتفك والجهاز المحدّد."</string>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index 4fb9ceed..2316baa 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -30,7 +30,7 @@
<string name="summary_app_streaming" msgid="295548145144086753">"%1$s ще има достъп до всичко, което се показва или възпроизвежда на телефона, включително аудиосъдържание, снимки, пароли и съобщения.<br/><br/>%1$s ще може да предава поточно приложения, докато не премахнете това разрешение."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Услуги за различни устройства"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> да предава поточно приложения между устройствата ви"</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> да показва и да предава поточно приложения между устройствата ви"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ иска разрешение от името на <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> да показва и да предава поточно приложения между устройствата ви"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Разрешете на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да осъществява достъп до тази информация от телефона ви"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 231d395..247d017 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -26,7 +26,7 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"uređaj"</string>
<string name="summary_glasses" msgid="2872254734959842579">"Aplikaciji će biti dozvoljen pristup ovim odobrenjima na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Dozvolite da aplikacija <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pristupa ovim informacijama s telefona"</string>
- <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Dozvoliti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da prenosi aplikacije telefona?"</string>
+ <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Dozvoliti uređaju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da prenosi aplikacije telefona?"</string>
<string name="summary_app_streaming" msgid="295548145144086753">"%1$s će imati pristup svemu što je vidljivo ili se reproducira na telefonu, uključujući zvuk, fotografije, lozinke i poruke.<br/><br/>%1$s će moći prenositi aplikacije dok ne uklonite pristup ovom odobrenju."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluga na više uređaja"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> zahtijeva odobrenje da prenosi aplikacije između vaših uređaja"</string>
@@ -59,7 +59,7 @@
<string name="permission_microphone" msgid="2152206421428732949">"Mikrofon"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"Zapisnici poziva"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Uređaji u blizini"</string>
- <string name="permission_media_routing_control" msgid="5498639511586715253">"Promijeni izlaz med. sadržaja"</string>
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"Promjena medijskog izlaza"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotografije i mediji"</string>
<string name="permission_notifications" msgid="4099418516590632909">"Obavještenja"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 94510e3..3acd179 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -27,7 +27,7 @@
<string name="summary_glasses" msgid="2872254734959842579">"Tato aplikace bude mít ve vašem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> povolený přístup k těmto oprávněním"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Povolte aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> přístup k těmto informacím z vašeho telefonu"</string>
<string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Povolit zařízení <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> streamovat aplikace telefonu?"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"Aplikace %1$s bude mít přístup ke všemu, co zobrazíte nebo přehrajete na telefonu, včetně zvuku, fotek, hesel a zpráv.<br/><br/>%1$s bude moci streamovat aplikace, dokud přístup k tomuto oprávnění neodeberete."</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"%1$s bude mít přístup ke všemu, co na telefonu zobrazíte nebo přehrajete, včetně zvuku, fotek, hesel a zpráv.<br/><br/>%1$s bude moci streamovat aplikace, dokud přístup k tomuto oprávnění neodeberete."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pro více zařízení"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> oprávnění ke streamování aplikací mezi zařízeními"</string>
<string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> oprávnění k zobrazení a streamování obsahu mezi zařízeními"</string>
@@ -39,7 +39,7 @@
<string name="helper_summary_computer" msgid="8774832742608187072">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> oprávnění k přístupu k fotkám, médiím a oznámením v telefonu"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Povolit zařízení <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> podniknout tuto akci?"</string>
<string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Povolit zařízení <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> streamovat aplikace a systémové funkce telefonu?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"Aplikace %1$s bude mít přístup ke všemu, co zobrazíte nebo přehrajete na telefonu, včetně zvuku, fotek, platebních údajů, hesel a zpráv.<br/><br/>%1$s bude moci streamovat aplikace a systémové funkce, dokud přístup k tomuto oprávnění neodeberete."</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s bude mít přístup ke všemu, co na telefonu zobrazíte nebo přehrajete, včetně zvuku, fotek, platebních údajů, hesel a zpráv.<br/><br/>%1$s bude moci streamovat aplikace a systémové funkce, dokud přístup k tomuto oprávnění neodeberete."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> žádá jménem vašeho zařízení <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o oprávnění streamovat aplikace a další systémové funkce do zařízení v okolí"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string>
<string name="summary_generic" msgid="1761976003668044801">"Tato aplikace bude moci synchronizovat údaje, jako je jméno volajícího, mezi vaším telefonem a vybraným zařízením"</string>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 6b38bff..9d0846c 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -28,7 +28,7 @@
<string name="title_app_streaming" msgid="2270331024626446950">"Giv <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> adgang til disse oplysninger fra din telefon"</string>
<string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Vil du give <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tilladelse til at streame din telefons apps?"</string>
<string name="summary_app_streaming" msgid="295548145144086753">"%1$s har adgang til alt, der er synligt eller afspilles på telefonen, herunder lyd, billeder, adgangskoder og beskeder.<br/><br/>%1$s kan streame apps, indtil du fjerner adgangen til denne tilladelse."</string>
- <string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjenester, som kan tilsluttes en anden enhed"</string>
+ <string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjenester til flere enheder"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> til at streame apps mellem dine enheder"</string>
<string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> til at vise og streame apps mellem dine enheder"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index 65e923c..3f730fc 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -26,11 +26,11 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"Gerät"</string>
<string name="summary_glasses" msgid="2872254734959842579">"Diese App darf dann auf diese Berechtigungen auf deinem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> zugreifen:"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> Zugriff auf diese Informationen von deinem Smartphone gewähren"</string>
- <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> erlauben, die Apps auf deinem Smartphone zu streamen?"</string>
+ <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Zulassen, dass <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> die Apps auf deinem Smartphone streamt?"</string>
<string name="summary_app_streaming" msgid="295548145144086753">"%1$s hat dann Zugriff auf alle Inhalte, die auf deinem Smartphone sichtbar sind oder abgespielt werden, einschließlich Audio, Fotos, Passwörter und Nachrichten.<br/><br/>%1$s kann so lange Apps streamen, bis du diese Berechtigung entfernst."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Geräteübergreifende Dienste"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> um die Berechtigung zum Streamen von Apps zwischen deinen Geräten"</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> um die Berechtigung zum Anzeigen und Streamen von Apps zwischen deinen Geräten"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> um die Berechtigung, gegenseitig das Anzeigen und Streamen von Apps zu erlauben"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> Zugriff auf diese Informationen von deinem Smartphone gewähren"</string>
@@ -38,8 +38,8 @@
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play-Dienste"</string>
<string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet im Namen deines <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> um die Berechtigung zum Zugriff auf die Fotos, Medien und Benachrichtigungen deines Smartphones"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Darf das Gerät <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> diese Aktion ausführen?"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> erlauben, die Apps und Systemfunktionen auf deinem Smartphone zu streamen?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s hat dann Zugriff auf alle Inhalte, die auf deinem Smartphone sichtbar sind oder abgespielt werden, einschließlich Audio, Fotos, Zahlungsinformationen, Passwörter und Nachrichten.<br/><br/>%1$s kann so lange Apps und Systemfunktionen streamen, bis du diese Berechtigung entfernst."</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Zulassen, dass <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> die Apps und Systemfunktionen auf deinem Smartphone streamt?"</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s hat dann Zugriff auf alle Inhalte, die auf deinem Smartphone sichtbar sind oder abgespielt werden, einschließlich Audio, Fotos, Zahlungsinformationen, Passwörter und Nachrichten.<br/><br/>%1$s kann so lange Apps und Systemfunktionen streamen, bis du diese Berechtigung entfernst."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein Gerät (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) um die Berechtigung, Apps und andere Systemfunktionen auf Geräte in der Nähe zu streamen"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
<string name="summary_generic" msgid="1761976003668044801">"Diese App kann dann Daten wie den Namen eines Anrufers zwischen deinem Smartphone und dem ausgewählten Gerät synchronisieren"</string>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index c0d1888..15d97af 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -59,7 +59,7 @@
<string name="permission_microphone" msgid="2152206421428732949">"Micrófono"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"Registros de llamadas"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivos cercanos"</string>
- <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambia la salida multimedia"</string>
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambiar la salida multimedia"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotos y contenido multimedia"</string>
<string name="permission_notifications" msgid="4099418516590632909">"Notificaciones"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index b7a9ff6..e02cb04 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -26,21 +26,21 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"seade"</string>
<string name="summary_glasses" msgid="2872254734959842579">"Sellele rakendusele antakse need load teie seadmes <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Lubage rakendusel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pääseda teie telefonis juurde sellele teabele"</string>
- <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Kas lubate rakendusel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> oma telefoni rakendusi voogesitada?"</string>
+ <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Kas lubate seadmel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> oma telefoni rakendusi voogesitada?"</string>
<string name="summary_app_streaming" msgid="295548145144086753">"%1$s saab juurdepääsu kõigele, mis on telefonis nähtaval või esitatav, sh helile, fotodele, paroolidele ja sõnumitele.<br/><br/>%1$s saab rakendusi voogesitada kuni eemaldate juurdepääsu sellele loale."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Seadmeülesed teenused"</string>
- <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nimel luba teie seadmete vahel rakendusi voogesitada"</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nimel luba teie seadmete vahel rakendusi kuvada ja voogesitada"</string>
+ <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nimel luba teie seadmete vahel rakendusi voogesitada"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nimel luba teie seadmete vahel rakendusi kuvada ja voogesitada"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Lubage rakendusel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pääseda teie telefonis juurde sellele teabele"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play teenused"</string>
- <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nimel luba pääseda juurde telefoni fotodele, meediale ja märguannetele"</string>
+ <string name="helper_summary_computer" msgid="8774832742608187072">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nimel luba pääseda juurde telefoni fotodele, meediale ja märguannetele"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Kas lubada seadmel <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> teha seda toimingut?"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Kas lubate rakendusel <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> oma telefoni rakenduste ja süsteemifunktsioonidel voogesitada?"</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Kas lubate seadmel <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> oma telefoni rakendusi ja süsteemifunktsioone voogesitada?"</string>
<string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s saab juurdepääsu kõigele, mis on teie telefonis nähtav või esitatav, sh heli, fotod, makseteave, paroolid ja sõnumid.<br/><br/>%1$s saab voogesitada rakendusi ja süsteemifunktsioone, kuni eemaldate juurdepääsu sellele loale."</string>
- <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nimel luba voogesitada rakendusi ja muid süsteemi funktsioone läheduses olevatesse seadmetesse"</string>
+ <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nimel luba voogesitada rakendusi ja muid süsteemi funktsioone läheduses olevatesse seadmetesse"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"seade"</string>
<string name="summary_generic" msgid="1761976003668044801">"See rakendus saab sünkroonida teavet, näiteks helistaja nime, teie telefoni ja valitud seadme vahel"</string>
<string name="consent_yes" msgid="8344487259618762872">"Luba"</string>
@@ -59,7 +59,7 @@
<string name="permission_microphone" msgid="2152206421428732949">"Mikrofon"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"Kõnelogid"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Läheduses olevad seadmed"</string>
- <string name="permission_media_routing_control" msgid="5498639511586715253">"Muutke meediaväljundit"</string>
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"Meediaväljundi muutmine"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotod ja meedia"</string>
<string name="permission_notifications" msgid="4099418516590632909">"Märguanded"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Rakendused"</string>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index d0dee9b..ca84970 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -26,8 +26,8 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"gailua"</string>
<string name="summary_glasses" msgid="2872254734959842579">"Baimen hauek erabili ahalko ditu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>n aplikazioak:"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Eman informazioa telefonotik hartzeko baimena <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari"</string>
- <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari zure telefonoko aplikazioak zuzenean igortzeko baimena eman nahi diozu?"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"%1$s aplikazioak telefonoan ikusgai dagoen edo erreproduzitzen den eduki guztia atzitu ahal izango du, audioa, argazkiak, pasahitzak eta mezuak barne.<br/><br/>%1$s aplikazioak zuzenean igortzeko gai izango da, baimen hori kentzen diozun arte."</string>
+ <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gailuari zure telefonoko aplikazioak zuzenean igortzeko baimena eman nahi diozu?"</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"%1$s gailuak telefonoan ikusgai dagoen edo erreproduzitzen den eduki guztia atzitu ahal izango du, audioa, argazkiak, pasahitzak eta mezuak barne.<br/><br/>%1$s aplikazioak zuzenean igortzeko gai izango da, baimen hori kentzen diozun arte."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Gailuarteko zerbitzuak"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"Gailu batetik bestera aplikazioak igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> gailuaren izenean"</string>
<string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Aplikazioak gailuen artean bistaratzeko eta zuzenean igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> gailuaren izenean"</string>
@@ -38,8 +38,8 @@
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
<string name="helper_summary_computer" msgid="8774832742608187072">"Telefonoko argazkiak, multimedia-edukia eta jakinarazpenak erabiltzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> gailuaren izenean"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Ekintza hau gauzatzeko baimena eman nahi diozu <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> aplikazioari?"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> aplikazioari zure telefonoko aplikazioak eta sistemaren eginbideak zuzenean igortzeko baimena eman nahi diozu?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s aplikazioak telefonoan ikusgai dagoen edo erreproduzitzen den eduki guztia atzitu ahal izango du, audioa, argazkiak, ordainketa-informazioa, pasahitzak eta mezuak barne.<br/><br/>%1$s aplikazioak eta sistemaren eginbideak zuzenean igortzeko gai izango da, baimen hori kentzen diozun arte."</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> gailuari zure telefonoko aplikazioak eta sistemaren eginbideak zuzenean igortzeko baimena eman nahi diozu?"</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s gailuak telefonoan ikusgai dagoen edo erreproduzitzen den eduki guztia atzitu ahal izango du, audioa, argazkiak, ordainketa-informazioa, pasahitzak eta mezuak barne.<br/><br/>%1$s aplikazioak eta sistemaren eginbideak zuzenean igortzeko gai izango da, baimen hori kentzen diozun arte."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikazioak eta sistemaren beste eginbide batzuk inguruko gailuetara igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> gailuaren izenean"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
<string name="summary_generic" msgid="1761976003668044801">"Telefonoaren eta hautatutako gailuaren artean informazioa sinkronizatzeko gai izango da aplikazioa (esate baterako, deitzaileen izenak)"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index 9b55669..92d94f3 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -27,10 +27,10 @@
<string name="summary_glasses" msgid="2872254734959842579">"यह ऐप्लिकेशन, आपके <xliff:g id="DEVICE_NAME">%1$s</xliff:g> पर इन अनुमतियों को ऐक्सेस कर पाएगा"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को अपने फ़ोन से यह जानकारी ऐक्सेस करने की अनुमति दें"</string>
<string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को आपके फ़ोन के ऐप्लिकेशन स्ट्रीम करने की अनुमति देनी है?"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"%1$s के पास ऐसे कॉन्टेंट का ऐक्सेस होगा जो फ़ोन पर दिख रहा हो या चलाया गया हो. जैसे, ऑडियो, फ़ोटो, पासवर्ड, और मैसेज.<br/><br/>%1$s तब तक ऐप्लिकेशन स्ट्रीम करेगा, जब तक आप इस अनुमति को हटा न दें."</string>
- <string name="helper_title_app_streaming" msgid="4151687003439969765">"क्रॉस-डिवाइस से जुड़ी सेवाएं"</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"%1$s के पास ऐसे कॉन्टेंट का ऐक्सेस होगा जो फ़ोन पर दिख रहा हो या चल रहा हो. जैसे, ऑडियो, फ़ोटो, पासवर्ड, और मैसेज.<br/><br/>%1$s तब तक ऐप्लिकेशन स्ट्रीम करेगा, जब तक आप इस अनुमति को हटा न दें."</string>
+ <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> की ओर से, आपके डिवाइसों के बीच ऐप्लिकेशन स्ट्रीम करने की अनुमति मांग रहा है"</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> की ओर से, आपके डिवाइसों के बीच ऐप्लिकेशन दिखाने और स्ट्रीम करने की अनुमति मांग रहा है"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> की ओर से, एक डिवाइस के ऐप्लिकेशन, दूसरे डिवाइस पर दिखाने और स्ट्रीम करने की अनुमति मांग रहा है"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को अपने फ़ोन से यह जानकारी ऐक्सेस करने की अनुमति दें"</string>
@@ -39,7 +39,7 @@
<string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> की ओर से, आपने फ़ोन में मौजूद फ़ोटो, मीडिया, और सूचनाओं को ऐक्सेस करने की अनुमति मांग रहा है"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"क्या <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> को यह कार्रवाई करने की अनुमति देनी है?"</string>
<string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> को आपके फ़ोन के ऐप्लिकेशन और सिस्टम की सुविधाएं स्ट्रीम करने की अनुमति देनी है?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s के पास ऐसे कॉन्टेंट का ऐक्सेस होगा जो फ़ोन पर दिख रहा हो या चलाया गया हो. जैसे, ऑडियो, फ़ोटो, पेमेंट से जुड़ी जानकारी, पासवर्ड, और मैसेज.<br/><br/>%1$s तब तक ऐप्लिकेशन और सिस्टम की सुविधाओं को स्ट्रीम करेगा, जब तक आप इस अनुमति को हटा न दें."</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s के पास ऐसे कॉन्टेंट का ऐक्सेस होगा जो फ़ोन पर दिख रहा हो या चल रहा हो. जैसे, ऑडियो, फ़ोटो, पेमेंट की जानकारी, पासवर्ड, और मैसेज.<br/><br/>%1$s तब तक ऐप्लिकेशन और सिस्टम की सुविधाओं को स्ट्रीम करेगा, जब तक आप इस अनुमति को हटा न दें."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DEVICE_NAME">%2$s</xliff:g> की ओर से, ऐप्लिकेशन और दूसरे सिस्टम की सुविधाओं को आस-पास मौजूद डिवाइसों पर स्ट्रीम करने की अनुमति मांग रहा है"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string>
<string name="summary_generic" msgid="1761976003668044801">"यह ऐप्लिकेशन, आपके फ़ोन और चुने हुए डिवाइस के बीच जानकारी सिंक करेगा. जैसे, कॉल करने वाले व्यक्ति का नाम"</string>
@@ -59,7 +59,7 @@
<string name="permission_microphone" msgid="2152206421428732949">"माइक्रोफ़ोन"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"कॉल लॉग"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"आस-पास मौजूद डिवाइस"</string>
- <string name="permission_media_routing_control" msgid="5498639511586715253">"मीडिया आउटपुट बदलें"</string>
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"मीडिया आउटपुट में बदलाव करे"</string>
<string name="permission_storage" msgid="6831099350839392343">"फ़ोटो और मीडिया"</string>
<string name="permission_notifications" msgid="4099418516590632909">"सूचनाएं"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"ऐप्लिकेशन"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index 1c9c218..4985ae3c 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -59,7 +59,7 @@
<string name="permission_microphone" msgid="2152206421428732949">"Mikrofon"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"Hívásnaplók"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Közeli eszközök"</string>
- <string name="permission_media_routing_control" msgid="5498639511586715253">"Médiakiment módosítása"</string>
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"Médiakimenet módosítása"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotók és médiatartalmak"</string>
<string name="permission_notifications" msgid="4099418516590632909">"Értesítések"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Alkalmazások"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index c4de3cd..a655a3ec 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -26,8 +26,8 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"սարք"</string>
<string name="summary_glasses" msgid="2872254734959842579">"Այս հավելվածը կստանա հետևյալ թույլտվությունները ձեր <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ում"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Թույլատրեք <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին օգտագործել այս տեղեկությունները ձեր հեռախոսից"</string>
- <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Թույլատրե՞լ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին հեռարձակել ձեր հեռախոսի հավելվածները"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"%1$s հավելվածին հասանելի կլինի հեռախոսում ցուցադրվող կամ նվագարկվող բովանդակությունը՝ ներառյալ աուդիոն, լուսանկարները, գաղտնաբառերը և հաղորդագրությունները։<br/><br/>%1$s հավելվածը կկարողանա հավելվածներ հեռարձակել, քանի դեռ չեք չեղարկել այս թույլտվությունը։"</string>
+ <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Թույլատրե՞լ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-ին հեռարձակել ձեր հեռախոսի հավելվածները"</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"%1$s-ին հասանելի կլինի հեռախոսում ցուցադրվող կամ նվագարկվող բովանդակությունը՝ ներառյալ աուդիոն, լուսանկարները, գաղտնաբառերը և հաղորդագրությունները։<br/><br/>%1$s-ը կկարողանա հավելվածներ հեռարձակել, քանի դեռ չեք չեղարկել այս թույլտվությունը։"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Միջսարքային ծառայություններ"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր սարքերի միջև հավելվածներ հեռարձակելու համար"</string>
<string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր սարքերի միջև հավելվածներ հեռարձակելու համար"</string>
@@ -38,8 +38,8 @@
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play ծառայություններ"</string>
<string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր հեռախոսի լուսանկարները, մեդիաֆայլերն ու ծանուցումները տեսնելու համար"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Թույլատրե՞լ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> հավելվածին կատարել այս գործողությունը"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Թույլատրե՞լ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> սարքին հեռարձակել ձեր հեռախոսի հավելվածները և համակարգի գործառույթները"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s սարքին հասանելի կլինի ձեր հեռախոսում ցուցադրվող կամ նվագարկվող բովանդակությունը՝ ներառյալ աուդիոն, լուսանկարները, վճարային տեղեկությունները, գաղտնաբառերը և հաղորդագրությունները։<br/><br/>%1$s սարքը կկարողանա հավելվածներ և համակարգի գործառույթներ հեռարձակել, քանի դեռ չեք չեղարկել այս թույլտվությունը։"</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Թույլատրե՞լ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong>-ին հեռարձակել ձեր հեռախոսի հավելվածները և համակարգի գործառույթները"</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s-ին հասանելի կլինի ձեր հեռախոսում ցուցադրվող կամ նվագարկվող բովանդակությունը՝ ներառյալ աուդիոն, լուսանկարները, վճարային տեղեկությունները, գաղտնաբառերը և հաղորդագրությունները։<br/><br/>%1$s-ը կկարողանա հավելվածներ և համակարգի գործառույթներ հեռարձակել, քանի դեռ չեք չեղարկել այս թույլտվությունը։"</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DEVICE_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ մոտակա սարքերին հավելվածներ և համակարգի այլ գործառույթներ հեռարձակելու համար"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string>
<string name="summary_generic" msgid="1761976003668044801">"Այս հավելվածը կկարողանա համաժամացնել ձեր հեռախոսի և ընտրված սարքի տվյալները, օր․՝ զանգողի անունը"</string>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index b1c4f5b..1481ca1 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -26,11 +26,11 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"perangkat"</string>
<string name="summary_glasses" msgid="2872254734959842579">"Aplikasi ini akan diizinkan mengakses izin ini di <xliff:g id="DEVICE_NAME">%1$s</xliff:g> Anda"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengakses informasi ini dari ponsel Anda"</string>
- <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> melakukan streaming aplikasi di ponsel Anda?"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"%1$s akan memiliki akses ke konten apa pun yang ditampilkan atau dimainkan di ponsel, termasuk audio, foto, sandi, dan pesan.<br/><br/>%1$s akan dapat melakukan streaming aplikasi hingga Anda menghapus izin ini."</string>
+ <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> menstreaming aplikasi di ponsel Anda?"</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"%1$s akan memiliki akses ke apa pun yang ditampilkan atau diputar di ponsel, termasuk audio, foto, sandi, dan pesan.<br/><br/>%1$s akan dapat menstreaming aplikasi hingga Anda menghapus izin ini."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Layanan lintas perangkat"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> untuk menstreaming aplikasi di antara perangkat Anda"</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> menggantikan <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> meminta izin untuk menampilkan dan melakukan streaming aplikasi di antara perangkat Anda"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> untuk menampilkan dan menstreaming aplikasi di antara perangkat Anda"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> mengakses informasi ini dari ponsel Anda"</string>
@@ -38,8 +38,8 @@
<string name="helper_title_computer" msgid="4671071173916176037">"Layanan Google Play"</string>
<string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> untuk mengakses foto, media, dan notifikasi ponsel Anda"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Izinkan <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> melakukan tindakan ini?"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Izinkan <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> melakukan streaming aplikasi dan mengakses fitur sistem di ponsel Anda?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s akan memiliki akses ke konten apa pun yang ditampilkan atau dimainkan di ponsel Anda, termasuk audio, foto, info pembayaran, sandi, dan pesan.<br/><br/>%1$s akan dapat melakukan streaming aplikasi dan mengakses fitur sistem hingga Anda menghapus izin ini."</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Izinkan <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> menstreaming aplikasi dan fitur sistem di ponsel Anda?"</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s akan memiliki akses ke apa pun yang ditampilkan atau diputar di ponsel Anda, termasuk audio, foto, info pembayaran, sandi, dan pesan.<br/><br/>%1$s akan dapat menstreaming aplikasi dan fitur sistem hingga Anda menghapus izin ini."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk menstreaming aplikasi dan fitur sistem lainnya ke perangkat di sekitar"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string>
<string name="summary_generic" msgid="1761976003668044801">"Aplikasi ini akan dapat menyinkronkan info, seperti nama penelepon, antara ponsel dan perangkat yang dipilih"</string>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index f1eb5347..ce8feb5 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -30,7 +30,7 @@
<string name="summary_app_streaming" msgid="295548145144086753">"ל-%1$s תהיה גישה לכל מה שרואים או מפעילים בטלפון, כולל אודיו, תמונות, סיסמאות והודעות.<br/><br/>ל-%1$s תהיה אפשרות לשדר אפליקציות עד שהגישה להרשאה הזו תוסר."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"שירותים למספר מכשירים"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור המכשיר <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> כדי לשדר אפליקציות בין המכשירים שלך"</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה למכשיר <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> כדי להציג ולשדר אפליקציות בין המכשירים שלך"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"האפליקציה \'<xliff:g id="APP_NAME">%1$s</xliff:g>\' מבקשת הרשאה למכשיר <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> כדי להציג ולשדר אפליקציות בין המכשירים שלך"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"מתן אישור לאפליקציה <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לגשת למידע הזה מהטלפון שלך"</string>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index 7aace82..7dbcef0 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -30,7 +30,7 @@
<string name="summary_app_streaming" msgid="295548145144086753">"ಆಡಿಯೋ, ಫೋಟೋಗಳು, ಪಾಸ್ವರ್ಡ್ಗಳು ಮತ್ತು ಸಂದೇಶಗಳು ಸೇರಿದಂತೆ ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿ ಗೋಚರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಆಗುವ ಯಾವುದೇ ಕಂಟೆಂಟ್ಗೆ %1$s ಆ್ಯಕ್ಸೆಸ್ ಹೊಂದಿರುತ್ತದೆ.<br/><br/>ನೀವು ಈ ಅನುಮತಿಗೆ ಇರುವ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ತೆಗೆದುಹಾಕುವವರೆಗೆ %1$s ಗೆ ಆ್ಯಪ್ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"ಕ್ರಾಸ್-ಡಿವೈಸ್ ಸೇವೆಗಳು"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"ನಿಮ್ಮ ಸಾಧನಗಳ ನಡುವೆ ಆ್ಯಪ್ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ನ ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸಿಕೊಳ್ಳುತ್ತಿದೆ"</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"ನಿಮ್ಮ ಸಾಧನಗಳ ನಡುವೆ ಆ್ಯಪ್ಗಳನ್ನು ಪ್ರದರ್ಶಿಸಲು ಮತ್ತು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸುತ್ತಿದೆ"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"ನಿಮ್ಮ ಸಾಧನಗಳ ನಡುವೆ ಆ್ಯಪ್ಗಳನ್ನು ಪ್ರದರ್ಶಿಸಲು ಮತ್ತು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸುತ್ತಿವೆ"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"ನಿಮ್ಮ ಫೋನ್ ಮೂಲಕ ಈ ಮಾಹಿತಿಯನ್ನು ಪ್ರವೇಶಿಸಲು <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಗೆ ಅನುಮತಿಸಿ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 86679ac..925f21b 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -30,7 +30,7 @@
<string name="summary_app_streaming" msgid="295548145144086753">"%1$s에서 오디오, 사진, 비밀번호, 메시지 등 휴대전화에 표시되거나 휴대전화에서 재생되는 모든 항목에 액세스할 수 있습니다.<br/><br/>이 권한에 대한 액세스를 삭제할 때까지 %1$s에서 앱을 스트리밍할 수 있습니다."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"교차 기기 서비스"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 대신 기기 간에 앱을 스트리밍할 수 있는 권한을 요청하고 있습니다."</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 대신 기기 간 앱을 표시하고 스트리밍할 권한을 요청하고 있습니다."</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 대신 연결된 기기의 앱을 표시하고 스트리밍할 권한을 요청하고 있습니다."</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>이 휴대전화에서 이 정보에 액세스하도록 허용"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index 932b5c5..1b72477 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -26,8 +26,8 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"түзмөк"</string>
<string name="summary_glasses" msgid="2872254734959842579">"Бул колдонмого <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүңүздө төмөнкүлөрдү аткарууга уруксат берилет"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string>
- <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна телефонуңуздагы колдонмолорду алып ойнотууга уруксат бересизби?"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"%1$s телефондо көрүнгөн же ойнотулган бардык нерселерге, анын ичинде аудио, сүрөттөр, сырсөздөр жана билдирүүлөргө кире алат. Бул уруксатты алып салмайынча, <br/><br/>%1$s колдонмолорду алып ойното алат."</string>
+ <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна телефонуңуздагы колдонмолорду өткөрүүгө уруксат бересизби?"</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"%1$s телефондо көрүнгөн же ойнотулган аудиофайлдар, сүрөттөр, сырсөздөр жана билдирүүлөр сыяктуу нерселерди көрө алат. Бул уруксатты алып салмайынча, <br/><br/>%1$s колдонмолорду өткөрө берет."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Түзмөктөр аралык кызматтар"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрүңүздүн ортосунда колдонмолорду алып ойнотууга уруксат сурап жатат"</string>
<string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрдүн ортосунда колдонмолорду көрсөтүү жана алып ойнотуу үчүн уруксат сурап жатат"</string>
@@ -38,8 +38,8 @@
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play кызматтары"</string>
<string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> түзмөгүңүздүн атынан телефондогу сүрөттөрдү, медиа файлдарды жана билдирмелерди колдонууга уруксат сурап жатат"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> түзмөгүнө бул аракетти аткарууга уруксат бересизби?"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> колдонмосуна телефонуңуздагы колдонмолорду жана тутумдун функцияларын алып ойнотууга уруксат бересизби?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s телефонуңузда көрүнгөн же ойнотулган бардык нерселерге, анын ичинде аудио, сүрөттөр, төлөм маалыматы, сырсөздөр жана билдирүүлөргө кире алат. Бул уруксатты алып салмайынча, <br/><br/>%1$s колдонмолорду жана тутум функцияларын алып ойното алат."</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> колдонмосуна телефонуңуздагы колдонмолорду жана системалык функцияларды өткөргөнгө уруксат бересизби?"</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s телефонуңузда көрүнгөн же ойнотулган аудиофайлдар, сүрөттөр, төлөм маалыматы, сырсөздөр жана билдирүүлөр сыяктуу нерселерди көрө алат. Бул уруксатты алып салмайынча, <br/><br/>%1$s колдонмолорду жана системдик функцияларды өткөрө алат."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> түзмөгүңүздүн атынан жакын жердеги түзмөктөрдө колдонмолорду жана системанын башка функцияларын алып ойнотууга уруксат сурап жатат"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string>
<string name="summary_generic" msgid="1761976003668044801">"Бул колдонмо маалыматты шайкештире алат, мисалы, чалып жаткан кишинин атын телефон жана тандалган түзмөк менен шайкештирет"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index fe6e4cc..0c4bfcb 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -27,7 +27,7 @@
<string name="summary_glasses" msgid="2872254734959842579">"Энэ апп таны <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-н эдгээр зөвшөөрөлд хандах эрхтэй байх болно"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-д таны утаснаас энэ мэдээлэлд хандахыг зөвшөөрнө үү"</string>
<string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Таны утасны аппуудыг дамжуулахыг <strong><xliff:g id="APP_NAME">%1$s</xliff:g>-д</strong> зөвшөөрөх үү?"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"%1$s аудио, зураг, нууц үг болон мессежүүдийг оруулаад утсан дээр харагдсан эсвэх тоглуулсан аливаа зүйлд хандах эрхтэй болно.<br/><br/>%1$s таныг энэ зөвшөөрлийг хасах хүртэл аппуудыг дамжуулах боломжтой байх болно."</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"%1$s аудио, зураг, нууц үг болон мессежүүдийг зэрэг утсан дээр харагдсан эсвэл тоглуулсан аливаа зүйлд хандах эрхтэй болно.<br/><br/>%1$s таныг энэ зөвшөөрлийг хасах хүртэл аппуудыг дамжуулах боломжтой байх болно."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Төхөөрөмж хоорондын үйлчилгээ"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"Таны төхөөрөмжүүд хооронд апп дамжуулахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-н өмнөөс зөвшөөрөл хүсэж байна"</string>
<string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-н өмнөөс таны төхөөрөмжүүдийн хооронд аппууд үзүүлж, дамжуулах зөвшөөрлийг хүсэж байна"</string>
@@ -39,7 +39,7 @@
<string name="helper_summary_computer" msgid="8774832742608187072">"Таны утасны зураг, медиа болон мэдэгдэлд хандахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-н өмнөөс зөвшөөрөл хүсэж байна"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong>-д энэ үйлдлийг хийхийг зөвшөөрөх үү?"</string>
<string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Таны утасны апп болон системийн онцлогуудыг дамжуулахыг <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g>-д</strong> зөвшөөрөх үү?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s аудио, зураг, төлбөрийн мэдээлэл, нууц үг болон мессежүүдийг оруулаад утсан дээр харагдсан эсвэх тоглуулсан аливаа зүйлд хандах эрхтэй болно.<br/><br/>%1$s таныг энэ зөвшөөрлийг хасах хүртэл апп болон системийн онцлогуудыг дамжуулах боломжтой байх болно."</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s аудио, зураг, төлбөрийн мэдээлэл, нууц үг болон мессеж зэрэг утсан дээр харагдсан эсвэл тоглуулсан аливаа зүйлд хандах эрхтэй болно.<br/><br/>%1$s таныг энэ зөвшөөрлийг хасах хүртэл апп болон системийн онцлогуудыг дамжуулах боломжтой байх болно."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-н өмнөөс аппууд болон системийн бусад онцлогийг ойролцоох төхөөрөмжүүд рүү дамжуулах зөвшөөрөл хүсэж байна"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string>
<string name="summary_generic" msgid="1761976003668044801">"Энэ апп залгаж буй хүний нэр зэрэг мэдээллийг таны утас болон сонгосон төхөөрөмжийн хооронд синк хийх боломжтой болно"</string>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index d817df2..4605c18 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -24,7 +24,7 @@
<string name="summary_watch" msgid="7962014927042971830">"Denne appen får tillatelse til å synkronisere informasjon som navnet til noen som ringer, og har disse tillatelsene på <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="confirmation_title_glasses" msgid="8288346850537727333">"Vil du la <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> administrere <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string>
<string name="profile_name_glasses" msgid="3506504967216601277">"enheten"</string>
- <string name="summary_glasses" msgid="2872254734959842579">"Denne appen får disse tillatelsene på <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="summary_glasses" msgid="2872254734959842579">"Denne appen får disse tillatelsene på enheten din (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Gi <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tilgang til denne informasjonen fra telefonen din"</string>
<string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Vil du tillate at <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> strømmer appene på telefonen?"</string>
<string name="summary_app_streaming" msgid="295548145144086753">"%1$s får tilgang til alt som er synlig eller spilles av på telefonen, inkludert lyd, bilder, passord og meldinger.<br/><br/>%1$s kan strømme apper til du fjerner denne tillatelsen."</string>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 8394a82..448362f 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -27,7 +27,7 @@
<string name="summary_glasses" msgid="2872254734959842579">"ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> \'ਤੇ ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੋਂ ਇਸ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
<string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"ਕੀ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"%1$s ਕੋਲ ਆਡੀਓ, ਫ਼ੋਟੋਆਂ, ਪਾਸਵਰਡਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਸਮੇਤ, ਫ਼ੋਨ \'ਤੇ ਦਿਖਾਈ ਦੇਣ ਵਾਲੀ ਜਾਂ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਕਿਸੇ ਵੀ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ।<br/><br/>%1$s ਐਪਾਂ ਨੂੰ ਉਦੋਂ ਤੱਕ ਸਟ੍ਰੀਮ ਕਰ ਸਕੇਗੀ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹਟਾ ਦਿੰਦੇ।"</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"%1$s ਕੋਲ ਆਡੀਓ, ਫ਼ੋਟੋਆਂ, ਪਾਸਵਰਡਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਸਮੇਤ, ਫ਼ੋਨ \'ਤੇ ਦਿਖਾਈ ਦੇਣ ਵਾਲੀ ਜਾਂ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਕਿਸੇ ਵੀ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ।<br/><br/>%1$s ਐਪਾਂ ਨੂੰ ਉਦੋਂ ਤੱਕ ਸਟ੍ਰੀਮ ਕਰ ਸਕੇਗਾ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹਟਾ ਦਿੰਦੇ।"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"ਕ੍ਰਾਸ-ਡੀਵਾਈਸ ਸੇਵਾਵਾਂ"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸਾਂ ਵਿਚਕਾਰ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string>
<string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸਾਂ ਵਿਚਕਾਰ ਐਪਾਂ ਨੂੰ ਦਿਖਾਉਣ ਅਤੇ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string>
@@ -39,7 +39,7 @@
<string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ ਫ਼ੋਨ ਦੀਆਂ ਫ਼ੋਟੋਆਂ, ਮੀਡੀਆ ਅਤੇ ਸੂਚਨਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"ਕੀ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> ਨੂੰ ਇਹ ਕਾਰਵਾਈ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
<string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"ਕੀ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਅਤੇ ਸਿਸਟਮ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s ਕੋਲ ਆਡੀਓ, ਫ਼ੋਟੋਆਂ, ਭੁਗਤਾਨ ਜਾਣਕਾਰੀ, ਪਾਸਵਰਡਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਸਮੇਤ, ਫ਼ੋਨ \'ਤੇ ਦਿਖਾਈ ਦੇਣ ਵਾਲੀ ਜਾਂ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਕਿਸੇ ਵੀ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ।<br/><br/>%1$s ਐਪਾਂ ਨੂੰ ਉਦੋਂ ਤੱਕ ਸਟ੍ਰੀਮ ਅਤੇ ਸਿਸਟਮ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਸਕੇਗੀ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹਟਾ ਦਿੰਦੇ।"</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s ਕੋਲ ਆਡੀਓ, ਫ਼ੋਟੋਆਂ, ਭੁਗਤਾਨ ਜਾਣਕਾਰੀ, ਪਾਸਵਰਡਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਸਮੇਤ, ਫ਼ੋਨ \'ਤੇ ਦਿਖਾਈ ਦੇਣ ਵਾਲੀ ਜਾਂ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਕਿਸੇ ਵੀ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ।<br/><br/>%1$s ਐਪਾਂ ਨੂੰ ਉਦੋਂ ਤੱਕ ਸਟ੍ਰੀਮ ਅਤੇ ਸਿਸਟਮ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਸਕੇਗਾ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹਟਾ ਦਿੰਦੇ।"</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸਾਂ \'ਤੇ ਐਪਾਂ ਅਤੇ ਹੋਰ ਸਿਸਟਮ ਸੰਬੰਧੀ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
<string name="summary_generic" msgid="1761976003668044801">"ਇਹ ਐਪ ਤੁਹਾਡੇ ਫ਼ੋਨ ਅਤੇ ਚੁਣੇ ਗਏ ਡੀਵਾਈਸ ਵਿਚਕਾਰ ਕਾਲਰ ਦੇ ਨਾਮ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਿੰਕ ਕਰ ਸਕੇਗੀ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index 945f849..7579678 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -30,7 +30,7 @@
<string name="summary_app_streaming" msgid="295548145144086753">"%1$s będzie mieć dostęp do wszystkiego, co jest widoczne i odtwarzane na telefonie, w tym do dźwięku, zdjęć, haseł i wiadomości.<br/><br/>%1$s będzie w stanie strumieniować aplikacje, dopóki nie usuniesz dostępu do tego uprawnienia."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Usługi na innym urządzeniu"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> o uprawnienia dotyczące strumieniowego odtwarzania treści z aplikacji na innym urządzeniu"</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> o pozwolenie na wyświetlanie i strumieniowanie aplikacji między Twoimi urządzeniami"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> o pozwolenie na wyświetlanie i strumieniowanie aplikacji między Twoimi urządzeniami"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Zezwól aplikacji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na dostęp do tych informacji na Twoim telefonie"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index 289d6e0..eb7b533 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -30,7 +30,7 @@
<string name="summary_app_streaming" msgid="295548145144086753">"O app %1$s terá acesso a tudo o que estiver visível ou for aberto no smartphone, incluindo áudios, fotos, senhas e mensagens. O app <br/><br/>%1$s poderá fazer o streaming de apps até que você remova o acesso a essa permissão."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para mostrar e fazer streaming de apps entre seus dispositivos"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para mostrar e fazer streaming de apps entre seus dispositivos"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Autorizar que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse estas informações do smartphone"</string>
@@ -38,8 +38,8 @@
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
<string name="helper_summary_computer" msgid="8774832742608187072">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para acessar fotos, mídia e notificações do smartphone"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Permitir que o dispositivo <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> realize esta ação?"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Permitir que o dispositivo <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> faça streaming dos apps e recursos do sistema do smartphone?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"O app %1$s terá acesso a tudo o que estiver visível ou for aberto no smartphone, incluindo áudios, fotos, informações de pagamento, senhas e mensagens. O app <br/><br/>%1$s poderá fazer o streaming de apps e recursos do sistema até que você remova o acesso a essa permissão."</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Permitir que <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> faça streaming de apps e recursos do sistema do smartphone?"</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"O %1$s terá acesso a tudo o que estiver visível ou for aberto no smartphone, incluindo áudios, fotos, informações de pagamento, senhas e mensagens. <br/><br/>O %1$s poderá acessar e transferir informações de apps e recursos do sistema até que você remova essa permissão."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps e de outros recursos do sistema para dispositivos por perto"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="summary_generic" msgid="1761976003668044801">"O app poderá sincronizar informações, como o nome de quem está ligando, entre seu smartphone e o dispositivo escolhido"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
index 9d94de7..c951334 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -26,8 +26,8 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string>
<string name="summary_glasses" msgid="2872254734959842579">"Esta app vai poder aceder a estas autorizações no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Permita que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aceda a estas informações do seu telemóvel"</string>
- <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Permitir que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> faça stream das apps do telemóvel?"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"%1$s vai ter acesso a tudo o que seja visível ou reproduzido no telemóvel, incluindo áudio, fotos, palavras-passe e mensagens.<br/><br/>%1$s vai poder fazer stream de apps até remover o acesso a esta autorização."</string>
+ <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Permitir que o dispositivo <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> faça stream das apps do telemóvel?"</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"O dispositivo %1$s vai ter acesso a tudo o que seja visível ou reproduzido no telemóvel, incluindo áudio, fotos, palavras-passe e mensagens.<br/><br/>O %1$s vai poder fazer stream de apps até remover o acesso a esta autorização."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para fazer stream de apps entre os seus dispositivos"</string>
<string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para apresentar e fazer stream de apps entre os seus dispositivos"</string>
@@ -39,7 +39,7 @@
<string name="helper_summary_computer" msgid="8774832742608187072">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para aceder às fotos, ao conteúdo multimédia e às notificações do seu telemóvel"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Permitir que o dispositivo <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> faça esta ação?"</string>
<string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Permitir que o dispositivo <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> faça stream das apps e das funcionalidades do sistema do telemóvel?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s vai ter acesso a tudo o que seja visível ou reproduzido no telemóvel, incluindo áudio, fotos, informações de pagamento, palavras-passe e mensagens.<br/><br/>%1$s vai poder fazer stream de apps e funcionalidades do sistema até remover o acesso a esta autorização."</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"O dispositivo %1$s vai ter acesso a tudo o que seja visível ou reproduzido no telemóvel, incluindo áudio, fotos, informações de pagamento, palavras-passe e mensagens.<br/><br/>O %1$s vai poder fazer stream de apps e funcionalidades do sistema até remover o acesso a esta autorização."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer stream de apps e outras funcionalidades do sistema para dispositivos próximos"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="summary_generic" msgid="1761976003668044801">"Esta app vai poder sincronizar informações, como o nome do autor de uma chamada, entre o telemóvel e o dispositivo escolhido"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index 289d6e0..eb7b533 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -30,7 +30,7 @@
<string name="summary_app_streaming" msgid="295548145144086753">"O app %1$s terá acesso a tudo o que estiver visível ou for aberto no smartphone, incluindo áudios, fotos, senhas e mensagens. O app <br/><br/>%1$s poderá fazer o streaming de apps até que você remova o acesso a essa permissão."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para mostrar e fazer streaming de apps entre seus dispositivos"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para mostrar e fazer streaming de apps entre seus dispositivos"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Autorizar que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse estas informações do smartphone"</string>
@@ -38,8 +38,8 @@
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
<string name="helper_summary_computer" msgid="8774832742608187072">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para acessar fotos, mídia e notificações do smartphone"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Permitir que o dispositivo <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> realize esta ação?"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Permitir que o dispositivo <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> faça streaming dos apps e recursos do sistema do smartphone?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"O app %1$s terá acesso a tudo o que estiver visível ou for aberto no smartphone, incluindo áudios, fotos, informações de pagamento, senhas e mensagens. O app <br/><br/>%1$s poderá fazer o streaming de apps e recursos do sistema até que você remova o acesso a essa permissão."</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Permitir que <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> faça streaming de apps e recursos do sistema do smartphone?"</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"O %1$s terá acesso a tudo o que estiver visível ou for aberto no smartphone, incluindo áudios, fotos, informações de pagamento, senhas e mensagens. <br/><br/>O %1$s poderá acessar e transferir informações de apps e recursos do sistema até que você remova essa permissão."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps e de outros recursos do sistema para dispositivos por perto"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="summary_generic" msgid="1761976003668044801">"O app poderá sincronizar informações, como o nome de quem está ligando, entre seu smartphone e o dispositivo escolhido"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index ce65c70..15456aa 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -26,11 +26,11 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"устройстве"</string>
<string name="summary_glasses" msgid="2872254734959842579">"Это приложение получит указанные разрешения на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Разрешите приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> получать эту информацию с вашего телефона"</string>
- <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Разрешить приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> транслировать приложения с вашего телефона?"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"Приложение \"%1$s\" получит доступ ко всему, что показывается или воспроизводится на телефоне, включая аудиофайлы, фотографии, пароли и сообщения.<br/><br/>Приложение \"%1$s\" сможет транслировать приложения, пока вы не отзовете это разрешение."</string>
- <string name="helper_title_app_streaming" msgid="4151687003439969765">"Сервисы стриминга приложений"</string>
+ <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Разрешить устройству <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> транслировать приложения с вашего телефона?"</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"У устройства \"%1$s\" будет доступ ко всему, что показывается или воспроизводится на телефоне, включая аудиофайлы, фотографии, пароли и сообщения.<br/><br/><br/>Устройство \"%1$s\" сможет транслировать приложения, пока вы не отзовете это разрешение."</string>
+ <string name="helper_title_app_streaming" msgid="4151687003439969765">"Сервисы для нескольких устройств"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запрашивает разрешение от имени вашего устройства <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>, чтобы транслировать приложения между устройствами."</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запрашивает разрешение от имени вашего устройства <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>, чтобы транслировать приложения между устройствами."</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" от имени вашего устройства \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" запрашивает разрешение на трансляцию приложений между устройствами."</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Разрешите приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> получать эту информацию с вашего телефона"</string>
@@ -38,8 +38,8 @@
<string name="helper_title_computer" msgid="4671071173916176037">"Сервисы Google Play"</string>
<string name="helper_summary_computer" msgid="8774832742608187072">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запрашивает разрешение от имени вашего устройства <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>, чтобы получить доступ к фотографиям, медиаконтенту и уведомлениям на телефоне."</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Разрешить приложению <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> выполнять это действие?"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Разрешить приложению <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> транслировать приложения и системные функции с вашего телефона?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"Приложение \"%1$s\" получит доступ ко всему, что показывается или воспроизводится на вашем телефоне, включая аудиофайлы, фотографии, платежные данные, пароли и сообщения.<br/><br/>Приложение \"%1$s\" сможет транслировать приложения и системные функции, пока вы не отзовете это разрешение."</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Разрешить устройству <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> транслировать приложения и системные функции с вашего телефона?"</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"У устройства \"%1$s\" будет доступ ко всему, что показывается или воспроизводится на вашем телефоне, включая аудиофайлы, фотографии, платежные данные, пароли и сообщения.<br/><br/>Устройство \"%1$s\" сможет транслировать приложения и системные функции, пока вы не отзовете это разрешение."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" от имени вашего устройства \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запрашивает разрешение транслировать приложения и системные функции на устройства поблизости."</string>
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
<string name="summary_generic" msgid="1761976003668044801">"Приложение сможет синхронизировать информацию между телефоном и выбранным устройством, например данные из журнала звонков."</string>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index bbee596..c0d27f1 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -26,11 +26,11 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"zariadenie"</string>
<string name="summary_glasses" msgid="2872254734959842579">"Táto aplikácia bude mať prístup k týmto povoleniam v zariadení <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Povoľte aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> prístup k týmto informáciám z vášho telefónu"</string>
- <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Chcete povoliť aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> streamovať aplikácie vo svojom telefóne?"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"%1$s bude mať prístup k všetkému obsahu viditeľnému alebo prehrávanému v telefóne vrátane zvuku, fotiek, hesiel a správ.<br/><br/>%1$s bude môcť streamovať aplikácie, kým prístup k tomuto povoleniu neodstránite."</string>
+ <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Chcete povoliť zariadeniu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> streamovať aplikácie telefónu?"</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"%1$s bude mať prístup k všetkému, čo v telefóne zobrazíte alebo prehrajete, vrátane zvuku, fotiek, hesiel a správ.<br/><br/>%1$s bude môcť streamovať aplikácie, kým toto povolenie neodstránite."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pre viacero zariadení"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje pre zariadenie <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> povolenie streamovať aplikácie medzi vašimi zariadeniami."</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje pre zariadenie <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> povolenie zobrazovať a streamovať aplikácie medzi zariadeniami"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje v mene zariadenia <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> povolenie zobrazovať a streamovať aplikácie medzi zariadeniami"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Povoľte aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> prístup k týmto informáciám z vášho telefónu"</string>
@@ -38,8 +38,8 @@
<string name="helper_title_computer" msgid="4671071173916176037">"Služby Google Play"</string>
<string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje pre zariadenie <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> povolenie na prístup k fotkám, médiám a upozorneniam vášho telefónu"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Chcete povoliť zariadeniu <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> vykonať túto akciu?"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Chcete povoliť aplikácii <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> streamovať aplikácie a systémové funkcie vo svojom telefóne?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s bude mať prístup k všetkému obsahu viditeľnému alebo prehrávanému v telefóne vrátane zvuku, fotiek, platobných údajov, hesiel a správ.<br/><br/>%1$s bude môcť streamovať aplikácie, kým prístup k tomuto povoleniu neodstránite."</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Chcete povoliť zariadeniu <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> streamovať aplikácie a systémové funkcie telefónu?"</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s bude mať prístup k všetkému, čo v telefóne zobrazíte alebo prehrajete, vrátane zvuku, fotiek, platobných údajov, hesiel a správ.<br/><br/>%1$s bude môcť streamovať aplikácie a systémové funkcie, kým toto povolenie neodstránite."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje pre zariadenie <xliff:g id="DEVICE_NAME">%2$s</xliff:g> povolenie streamovať aplikácie a ďalšie systémové funkcie do zariadení v okolí"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string>
<string name="summary_generic" msgid="1761976003668044801">"Táto aplikácia bude môcť synchronizovať informácie, napríklad meno volajúceho, medzi telefónom a vybraným zariadením"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index 346fbae..a895c25 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -28,7 +28,7 @@
<string name="title_app_streaming" msgid="2270331024626446950">"Dovolite, da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dostopa do teh podatkov v vašem telefonu"</string>
<string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Ali aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dovolite, da pretočno predvaja aplikacije telefona?"</string>
<string name="summary_app_streaming" msgid="295548145144086753">"Aplikacija %1$s bo imela dostop do vsega, kar je prikazano ali se predvaja v telefonu, vključno z zvokom, fotografijami, gesli in sporočili.<br/><br/>Aplikacija %1$s bo lahko pretočno predvajala aplikacije, dokler ne odstranite dostopa do tega dovoljenja."</string>
- <string name="helper_title_app_streaming" msgid="4151687003439969765">"Storitve za zunanje naprave"</string>
+ <string name="helper_title_app_streaming" msgid="4151687003439969765">"Storitve v več napravah"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>« zahteva dovoljenje za pretočno predvajanje aplikacij v vaših napravah."</string>
<string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>« zahteva dovoljenje za prikaz in pretočno predvajanje aplikacij v vaših napravah."</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 76f623a..63e8cb6 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -27,20 +27,20 @@
<string name="summary_glasses" msgid="2872254734959842579">"Këtij aplikacioni do t\'i lejohet qasja te këto leje në <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Lejo që <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> të ketë qasje në këtë informacion nga telefoni yt"</string>
<string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Të lejohet që <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> të transmetojë aplikacionet e telefonit tënd?"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"%1$s do të ketë qasje te çdo gjë që është e dukshme ose luhet në telefon, duke përfshirë audion, fotografitë, fjalëkalimet dhe mesazhet.<br/><br/>%1$s do të mund të transmetojë aplikacionet derisa ta heqësh qasjen për këtë leje."</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"%1$s do të ketë qasje te çdo gjë që është e dukshme ose që luhet në telefon, duke përfshirë audion, fotografitë, fjalëkalimet dhe mesazhet.<br/><br/>%1$s do të mund t\'i transmetojë aplikacionet derisa ta heqësh qasjen për këtë leje."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Shërbimet mes pajisjeve"</string>
- <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> për të transmetuar aplikacione ndërmjet pajisjeve të tua"</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> për të shfaqur dhe transmetuar aplikacionet mes pajisjeve të tua"</string>
+ <string name="helper_summary_app_streaming" msgid="2396773196949578425">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" po kërkon leje në emër të <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> për të transmetuar aplikacione ndërmjet pajisjeve të tua"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" po kërkon leje në emër të <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> për të shfaqur dhe transmetuar aplikacionet mes pajisjeve të tua"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Lejo që <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> të ketë qasje në këtë informacion nga telefoni yt"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Shërbimet e Google Play"</string>
- <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> për të marrë qasje te fotografitë, media dhe njoftimet e telefonit tënd"</string>
+ <string name="helper_summary_computer" msgid="8774832742608187072">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" po kërkon leje në emër të <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> për të marrë qasje te fotografitë, media dhe njoftimet e telefonit tënd"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Të lejohet që <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> të ndërmarrë këtë veprim?"</string>
<string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Të lejohet që <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> të transmetojë aplikacionet dhe veçoritë e sistemit të telefonit tënd?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s do të ketë qasje te çdo gjë që është e dukshme ose luhet në telefon, duke përfshirë audion, fotografitë, informacionet për pagesën, fjalëkalimet dhe mesazhet.<br/><br/>%1$s do të mund të transmetojë aplikacionet dhe veçoritë e sistemit derisa ta heqësh qasjen për këtë leje."</string>
- <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) tënde për të transmetuar aplikacione dhe veçori të tjera të sistemit te pajisjet në afërsi"</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s do të ketë qasje te çdo gjë që është e dukshme ose që luhet në telefon, duke përfshirë audion, fotografitë, informacionet për pagesën, fjalëkalimet dhe mesazhet.<br/><br/>%1$s do të mund t\'i transmetojë aplikacionet dhe veçoritë e sistemit derisa ta heqësh qasjen për këtë leje."</string>
+ <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" po kërkon leje në emër të (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) tënde për të transmetuar aplikacione dhe veçori të tjera të sistemit te pajisjet në afërsi"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
<string name="summary_generic" msgid="1761976003668044801">"Ky aplikacion do të mund të sinkronizojë informacione, si p.sh emrin e dikujt që po telefonon, mes telefonit tënd dhe pajisjes së zgjedhur."</string>
<string name="consent_yes" msgid="8344487259618762872">"Lejo"</string>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index ede266e..2643b2d 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -28,7 +28,7 @@
<string name="title_app_streaming" msgid="2270331024626446950">"మీ ఫోన్ నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> యాప్ను అనుమతించండి"</string>
<string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"మీ ఫోన్ యాప్లను స్ట్రీమ్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ను అనుమతించాలా?"</string>
<string name="summary_app_streaming" msgid="295548145144086753">"ఆడియో, ఫోటోలు, పాస్వర్డ్లు, మెసేజ్లతో సహా ఫోన్లో కనిపించే లేదా ప్లే అయ్యే దేనికైనా %1$sకు యాక్సెస్ ఉంటుంది.<br/><br/>మీరు ఈ అనుమతికి యాక్సెస్ను తీసివేసే వరకు %1$s యాప్లను స్ట్రీమ్ చేయగలదు."</string>
- <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string>
+ <string name="helper_title_app_streaming" msgid="4151687003439969765">"క్రాస్-డివైజ్ సర్వీసులు"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"మీ పరికరాల మధ్య యాప్లను స్ట్రీమ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> మీ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> తరఫున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string>
<string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"మీ పరికరాలలో యాప్లను డిస్ప్లే చేయడానికి, స్ట్రీమ్ చేయడానికి <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> తరఫున <xliff:g id="APP_NAME">%1$s</xliff:g> అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index 3efc299..5852657 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -26,7 +26,7 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"Cihaz"</string>
<string name="summary_glasses" msgid="2872254734959842579">"Bu uygulamanın <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazınızda şu izinlere erişmesine izin verilecek:"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasının, telefonunuzdaki bu bilgilere erişmesine izin verin"</string>
- <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> adlı uygulamanın telefonunuzdaki uygulamaları aktarmasına izin verilsin mi?"</string>
+ <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> adlı cihazın telefonunuzdaki uygulamaları aktarmasına izin verilsin mi?"</string>
<string name="summary_app_streaming" msgid="295548145144086753">"%1$s; ses, fotoğraflar, şifreler ve mesajlar da dahil olmak üzere telefonda görünen veya oynatılan her şeye erişebilecek.<br/><br/>%1$s siz bu iznin erişimini kaldırana kadar uygulamaları aktarabilecek."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Cihazlar arası hizmetler"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g>, cihazlarınız arasında uygulama akışı gerçekleştirmek için <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> cihazınız adına izin istiyor"</string>
@@ -38,7 +38,7 @@
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play Hizmetleri"</string>
<string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g>, telefonunuzdaki fotoğraf, medya ve bildirimlere erişmek için <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> cihazınız adına izin istiyor"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> cihazının bu işlemi yapmasına izin verilsin mi?"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> adlı uygulamanın telefonunuzdaki uygulamaları ve sistem özelliklerini aktarmasına izin verilsin mi?"</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> adlı cihazın telefonunuzdaki uygulamaları ve sistem özelliklerini aktarmasına izin verilsin mi?"</string>
<string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s; ses, fotoğraflar, ödeme bilgileri, şifreler ve mesajlar da dahil olmak üzere telefonunuzda görünen veya oynatılan her şeye erişebilecek.<br/><br/>%1$s siz bu iznin erişimini kaldırana kadar uygulamaları ve diğer sistem özelliklerini aktarabilecek."</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulaması <xliff:g id="DEVICE_NAME">%2$s</xliff:g> cihazınız adına uygulamaları ve diğer sistem özelliklerini yakındaki cihazlara aktarmak için izin istiyor"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index 109a011..d92a2e0 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -27,7 +27,7 @@
<string name="summary_glasses" msgid="2872254734959842579">"اس ایپ کو آپ کے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> پر ان اجازتوں تک رسائی کی اجازت ہوگی"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"اپنے فون سے ان معلومات تک رسائی حاصل کرنے کی <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو اجازت دیں"</string>
<string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"اجازت دیں<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> اپنے فون کی ایپس کو سلسلہ بندی کرنے کے لیے؟"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"%1$s کو فون پر دکھائی دینے والی یا چلائی جانے والی کسی بھی چیز تک رسائی حاصل ہوگی، بشمول آڈیو، تصاویر، پاس ورڈز اور پیغامات۔<br/><br/>%1$s اس وقت تک ایپس کو اسٹریم کر سکے گید جب تک آپ اس اجازت تک رسائی کو ہٹا دیتے ہیں۔"</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"%1$s کو فون پر دکھائی دینے والی یا چلائی جانے والی کسی بھی چیز تک رسائی حاصل ہوگی، بشمول آڈیو، تصاویر، پاس ورڈز اور پیغامات۔<br/><br/>اس وقت تک %1$s ایپس کو اسٹریم کر سکے گا جب تک آپ اس اجازت تک رسائی کو ہٹا نہیں دیتے۔"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"کراس ڈیوائس سروسز"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> کی جانب سے آپ کے آلات کے درمیان ایپس کی سلسلہ بندی کرنے کی اجازت کی درخواست کر رہی ہے"</string>
<string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> آپ کے <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> کی جانب سے آپ کے آلات کے درمیان ایپس کو ڈسپلے اور اسٹریم کرنے کے لیے اجازت کی درخواست کر رہی ہے"</string>
@@ -38,7 +38,7 @@
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play سروسز"</string>
<string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> کی جانب سے آپ کے فون کی تصاویر، میڈیا اور اطلاعات تک رسائی کی اجازت کی درخواست کر رہی ہے"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> کو یہ کارروائی انجام دینے کی اجازت دیں؟"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"اجازت دیں <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> اپنے فون کی ایپس اور سسٹم کی خصوصیات کو سلسلہ بندی کرنے کے لیے؟"</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"آپ کے فون کی ایپس اور سسٹم کی خصوصیات کو سلسلہ بندی کرنے کی <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> کو اجازت دیں؟"</string>
<!-- String.format failed for translation -->
<!-- no translation found for summary_nearby_device_streaming (4039565463149145573) -->
<skip />
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index 750ff0d..6daf4ff 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -26,11 +26,11 @@
<string name="profile_name_glasses" msgid="3506504967216601277">"设备"</string>
<string name="summary_glasses" msgid="2872254734959842579">"该应用将可以获得您<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上的以下权限"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”<strong></strong>访问您手机中的这项信息"</string>
- <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"允许 <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 流式传输手机的应用?"</string>
- <string name="summary_app_streaming" msgid="295548145144086753">"“%1$s”将能够访问手机上可见或播放的任何内容,包括音频、照片、密码和消息。<br/><br/>“%1$s”将能够流式传输应用,除非您撤消此访问权限。"</string>
+ <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"要允许 <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 流式传输手机的应用吗?"</string>
+ <string name="summary_app_streaming" msgid="295548145144086753">"%1$s 将能够访问手机上可见或播放的任何内容,包括音频、照片、密码和消息。<br/><br/>%1$s 将能够流式传输应用,除非您撤消此访问权限。"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"跨设备服务"</string>
<string name="helper_summary_app_streaming" msgid="2396773196949578425">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>请求在您的设备之间流式传输应用内容"</string>
- <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>请求在设备之间显示和流式传输应用"</string>
+ <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 请求在设备之间显示和流式传输应用"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"允许 <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 访问您手机中的这项信息"</string>
@@ -38,8 +38,8 @@
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play 服务"</string>
<string name="helper_summary_computer" msgid="8774832742608187072">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>请求访问您手机上的照片、媒体内容和通知"</string>
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"允许<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong>进行此操作?"</string>
- <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"允许 <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> 流式传输手机的应用和系统功能?"</string>
- <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"“%1$s”将能够访问手机上可见或播放的任何内容,包括音频、照片、付款信息、密码和消息。<br/><br/>“%1$s”将能够流式传输应用和系统功能,除非您撤消此访问权限。"</string>
+ <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"要允许 <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> 流式传输手机的应用和系统功能吗?"</string>
+ <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s 将能够访问手机上可见或播放的任何内容,包括音频、照片、付款信息、密码和消息。<br/><br/>%1$s 将能够流式传输应用和系统功能,除非您撤消此访问权限。"</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DEVICE_NAME">%2$s</xliff:g>请求将应用和其他系统功能流式传输到附近的设备"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"设备"</string>
<string name="summary_generic" msgid="1761976003668044801">"此应用将能在您的手机和所选设备之间同步信息,例如来电者的姓名"</string>
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index 0af1080..e8e24f4 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -40,6 +40,7 @@
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:gravity">center</item>
+ <item name="android:textDirection">locale</item>
<item name="android:layout_marginLeft">14dp</item>
<item name="android:layout_marginRight">14dp</item>
<item name="android:textSize">20sp</item>
@@ -53,6 +54,7 @@
<item name="android:layout_marginTop">18dp</item>
<item name="android:layout_marginLeft">18dp</item>
<item name="android:layout_marginRight">18dp</item>
+ <item name="android:textDirection">locale</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
diff --git a/packages/CrashRecovery/aconfig/flags.aconfig b/packages/CrashRecovery/aconfig/flags.aconfig
index 8627eac..15fdc52 100644
--- a/packages/CrashRecovery/aconfig/flags.aconfig
+++ b/packages/CrashRecovery/aconfig/flags.aconfig
@@ -2,7 +2,7 @@
flag {
name: "recoverability_detection"
- namespace: "package_watchdog"
+ namespace: "package_manager_service"
description: "Feature flag for recoverability detection"
bug: "310236690"
is_fixed_read_only: true
diff --git a/packages/CrashRecovery/services/java/com/android/util/BackgroundThread.java b/packages/CrashRecovery/services/java/com/android/util/BackgroundThread.java
deleted file mode 100644
index a6ae68f..0000000
--- a/packages/CrashRecovery/services/java/com/android/util/BackgroundThread.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-import android.annotation.NonNull;
-import android.os.Handler;
-import android.os.HandlerThread;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.concurrent.Executor;
-
-/**
- * Thread for asynchronous event processing. This thread is configured as
- * {@link android.os.Process#THREAD_PRIORITY_BACKGROUND}, which means fewer CPU
- * resources will be dedicated to it, and it will "have less chance of impacting
- * the responsiveness of the user interface."
- * <p>
- * This thread is best suited for tasks that the user is not actively waiting
- * for, or for tasks that the user expects to be executed eventually.
- *
- * @see com.android.internal.os.BackgroundThread
- *
- * TODO: b/326916057 depend on modules-utils-backgroundthread instead
- * @hide
- */
-public final class BackgroundThread extends HandlerThread {
- private static final Object sLock = new Object();
-
- @GuardedBy("sLock")
- private static BackgroundThread sInstance;
- @GuardedBy("sLock")
- private static Handler sHandler;
- @GuardedBy("sLock")
- private static HandlerExecutor sHandlerExecutor;
-
- private BackgroundThread() {
- super(BackgroundThread.class.getName(), android.os.Process.THREAD_PRIORITY_BACKGROUND);
- }
-
- @GuardedBy("sLock")
- private static void ensureThreadLocked() {
- if (sInstance == null) {
- sInstance = new BackgroundThread();
- sInstance.start();
- sHandler = new Handler(sInstance.getLooper());
- sHandlerExecutor = new HandlerExecutor(sHandler);
- }
- }
-
- /**
- * Get the singleton instance of this class.
- *
- * @return the singleton instance of this class
- */
- @NonNull
- public static BackgroundThread get() {
- synchronized (sLock) {
- ensureThreadLocked();
- return sInstance;
- }
- }
-
- /**
- * Get the singleton {@link Handler} for this class.
- *
- * @return the singleton {@link Handler} for this class.
- */
- @NonNull
- public static Handler getHandler() {
- synchronized (sLock) {
- ensureThreadLocked();
- return sHandler;
- }
- }
-
- /**
- * Get the singleton {@link Executor} for this class.
- *
- * @return the singleton {@link Executor} for this class.
- */
- @NonNull
- public static Executor getExecutor() {
- synchronized (sLock) {
- ensureThreadLocked();
- return sHandlerExecutor;
- }
- }
-}
diff --git a/packages/CrashRecovery/services/java/com/android/util/HandlerExecutor.java b/packages/CrashRecovery/services/java/com/android/util/HandlerExecutor.java
deleted file mode 100644
index 948ebcca..0000000
--- a/packages/CrashRecovery/services/java/com/android/util/HandlerExecutor.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-import android.annotation.NonNull;
-import android.os.Handler;
-
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
-
-/**
- * An adapter {@link Executor} that posts all executed tasks onto the given
- * {@link Handler}.
- *
- * TODO: b/326916057 depend on modules-utils-backgroundthread instead
- * @hide
- */
-public class HandlerExecutor implements Executor {
- private final Handler mHandler;
-
- public HandlerExecutor(@NonNull Handler handler) {
- mHandler = Objects.requireNonNull(handler);
- }
-
- @Override
- public void execute(Runnable command) {
- if (!mHandler.post(command)) {
- throw new RejectedExecutionException(mHandler + " is shutting down");
- }
- }
-}
diff --git a/packages/CredentialManager/res/values-af/strings.xml b/packages/CredentialManager/res/values-af/strings.xml
index a24134b3..b17293d 100644
--- a/packages/CredentialManager/res/values-af/strings.xml
+++ b/packages/CredentialManager/res/values-af/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Skep toegangsleutel om by <xliff:g id="APP_NAME">%1$s</xliff:g> aan te meld?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Stoor wagwoord om by <xliff:g id="APP_NAME">%1$s</xliff:g> aan te meld?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Stoor aanmeldinligting vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Gebruik jou skermslot om ’n toegangsleutel vir <xliff:g id="APP_NAME">%1$s</xliff:g> te skep?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Gebruik jou skermslot om ’n wagwoord vir <xliff:g id="APP_NAME">%1$s</xliff:g> te skep?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Gebruik jou skermslot om aanmeldinligting vir <xliff:g id="APP_NAME">%1$s</xliff:g> te stoor?"</string>
<string name="passkey" msgid="632353688396759522">"toegangsleutel"</string>
<string name="password" msgid="6738570945182936667">"wagwoord"</string>
<string name="passkeys" msgid="5733880786866559847">"toegangsleutels"</string>
diff --git a/packages/CredentialManager/res/values-am/strings.xml b/packages/CredentialManager/res/values-am/strings.xml
index 0554d81..4ee0788 100644
--- a/packages/CredentialManager/res/values-am/strings.xml
+++ b/packages/CredentialManager/res/values-am/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"ወደ <xliff:g id="APP_NAME">%1$s</xliff:g> ለመግባት የይለፍ ቁልፍ ይፈጠር?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"ወደ <xliff:g id="APP_NAME">%1$s</xliff:g> ለመግባት የይለፍ ቃል ይቀመጥ?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"የ<xliff:g id="APP_NAME">%1$s</xliff:g> የመግቢያ መረጃ ይቀመጥ?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"ለ<xliff:g id="APP_NAME">%1$s</xliff:g> የይለፍ ቁልፍ ለመፍጠር የማያ ገጽ መቆለፊያዎን መጠቀም ይፈልጋሉ?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"ለ<xliff:g id="APP_NAME">%1$s</xliff:g> የይለፍ ቃል ለመፍጠር የማያ ገጽ መቆለፊያዎን መጠቀም ይፈልጋሉ?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"የ<xliff:g id="APP_NAME">%1$s</xliff:g> መግቢያ መረጃን ለማስቀመጥ የማያ ገጽ መቆለፊያዎን መጠቀም ይፈልጋሉ?"</string>
<string name="passkey" msgid="632353688396759522">"የይለፍ ቁልፍ"</string>
<string name="password" msgid="6738570945182936667">"የይለፍ ቃል"</string>
<string name="passkeys" msgid="5733880786866559847">"የይለፍ ቁልፎች"</string>
diff --git a/packages/CredentialManager/res/values-ar/strings.xml b/packages/CredentialManager/res/values-ar/strings.xml
index 5e089fe..7e141c2 100644
--- a/packages/CredentialManager/res/values-ar/strings.xml
+++ b/packages/CredentialManager/res/values-ar/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"هل تريد إنشاء مفتاح مرور لتسجيل الدخول إلى \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"هل تريد حفظ كلمة المرور لتسجيل الدخول إلى \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"هل تريد حفظ معلومات تسجيل الدخول إلى \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"هل تريد استخدام قفل الشاشة لإنشاء مفتاح مرور لتطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"هل تريد استخدام قفل الشاشة لإنشاء كلمة مرور لتطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"هل تريد استخدام قفل الشاشة لحفظ معلومات تسجيل الدخول لتطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string>
<string name="passkey" msgid="632353688396759522">"مفتاح المرور"</string>
<string name="password" msgid="6738570945182936667">"كلمة المرور"</string>
<string name="passkeys" msgid="5733880786866559847">"مفاتيح المرور"</string>
diff --git a/packages/CredentialManager/res/values-as/strings.xml b/packages/CredentialManager/res/values-as/strings.xml
index 95a0e1b..cde9112 100644
--- a/packages/CredentialManager/res/values-as/strings.xml
+++ b/packages/CredentialManager/res/values-as/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g>ত ছাইন ইন কৰিবলৈ পাছকী সৃষ্টি কৰিবনে?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g>ত ছাইন ইন কৰিবলৈ পাছৱৰ্ড ছেভ কৰিবনে?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে ছাইন ইনৰ তথ্য ছেভ কৰিবনে?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে পাছকী সৃষ্টি কৰিবলৈ আপোনাৰ স্ক্ৰীন লক ব্যৱহাৰ কৰিবনে?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে পাছৱৰ্ড সৃষ্টি কৰিবলৈ আপোনাৰ স্ক্ৰীন লক ব্যৱহাৰ কৰিবনে?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে ছাইন ইনৰ তথ্য ছেভ কৰিবলৈ আপোনাৰ স্ক্ৰীন লক ব্যৱহাৰ কৰিবনে?"</string>
<string name="passkey" msgid="632353688396759522">"পাছকী"</string>
<string name="password" msgid="6738570945182936667">"পাছৱৰ্ড"</string>
<string name="passkeys" msgid="5733880786866559847">"পাছকী"</string>
diff --git a/packages/CredentialManager/res/values-az/strings.xml b/packages/CredentialManager/res/values-az/strings.xml
index 00a6718..1623ec4 100644
--- a/packages/CredentialManager/res/values-az/strings.xml
+++ b/packages/CredentialManager/res/values-az/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqinə daxil olmaq üçün giriş açarı yaradılsın?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqinə daxil olmaq üçün parol yadda saxlansın?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün giriş məlumatları yadda saxlansın?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün giriş açarı yaratmaq məqsədilə ekran kilidi istifadə edilsin?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün parol yaratmaq məqsədilə ekran kilidi istifadə edilsin?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün giriş məlumatını yadda saxlamaq məqsədilə ekran kilidi istifadə edilsin?"</string>
<string name="passkey" msgid="632353688396759522">"açar"</string>
<string name="password" msgid="6738570945182936667">"parol"</string>
<string name="passkeys" msgid="5733880786866559847">"açarlar"</string>
diff --git a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
index 390c774..23c021e 100644
--- a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Želite da napravite pristupni ključ da biste se prijavili u <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Želite da sačuvate lozinku da biste se prijavili u <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Želite da sačuvate podatke za prijavljivanje za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Želite da koristite otključavanje ekrana da biste napravili pristupni ključ za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Želite da koristite otključavanje ekrana da biste napravili lozinku za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Želite da koristite otključavanje ekrana da biste sačuvali podatke za prijavljivanje za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"pristupni kôd"</string>
<string name="password" msgid="6738570945182936667">"lozinka"</string>
<string name="passkeys" msgid="5733880786866559847">"pristupni kodovi"</string>
diff --git a/packages/CredentialManager/res/values-be/strings.xml b/packages/CredentialManager/res/values-be/strings.xml
index 6922e70..0b8ade7 100644
--- a/packages/CredentialManager/res/values-be/strings.xml
+++ b/packages/CredentialManager/res/values-be/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Стварыць ключ доступу для ўваходу ў праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Захаваць пароль для ўваходу ў праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Захаваць даныя для ўваходу ў праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Выкарыстаць сродак разблакіроўкі экрана, каб стварыць ключ доступу для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Выкарыстаць сродак разблакіроўкі экрана, каб стварыць пароль для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Выкарыстаць сродак разблакіроўкі экрана, каб захаваць даныя для ўваходу для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
<string name="passkey" msgid="632353688396759522">"ключ доступу"</string>
<string name="password" msgid="6738570945182936667">"пароль"</string>
<string name="passkeys" msgid="5733880786866559847">"ключы доступу"</string>
diff --git a/packages/CredentialManager/res/values-bg/strings.xml b/packages/CredentialManager/res/values-bg/strings.xml
index 3d33c8f..6a79b11 100644
--- a/packages/CredentialManager/res/values-bg/strings.xml
+++ b/packages/CredentialManager/res/values-bg/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Искате ли да създадете ключ за достъп, с който да влизате в(ъв) <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Искате ли да запазите паролата за влизане в(ъв) <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Искате ли да запазите данните за вход за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Искате ли да използвате опцията си за заключване на екрана, за да създадете ключ за достъп за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Искате ли да използвате опцията си за заключване на екрана, за да създадете парола за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Искате ли да използвате опцията си за заключване на екрана, за да запазите данните за вход за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"код за достъп"</string>
<string name="password" msgid="6738570945182936667">"парола"</string>
<string name="passkeys" msgid="5733880786866559847">"ключове за достъп"</string>
diff --git a/packages/CredentialManager/res/values-bn/strings.xml b/packages/CredentialManager/res/values-bn/strings.xml
index fe42ed6..76fc0a7 100644
--- a/packages/CredentialManager/res/values-bn/strings.xml
+++ b/packages/CredentialManager/res/values-bn/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপে সাইন-ইন করার জন্য পাসকী তৈরি করবেন?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপে সাইন-ইন করার জন্য পাসওয়ার্ড সেভ করবেন?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপের জন্য সাইন-ইন সংক্রান্ত তথ্য সেভ করবেন?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপের জন্য পাসকী তৈরি করতে আপনার স্ক্রিন লক ব্যবহার করতে চান?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপের জন্য পাসওয়ার্ড তৈরি করতে আপনার স্ক্রিন লক ব্যবহার করতে চান?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপের জন্য সাইন-ইন সংক্রান্ত তথ্য সেভ করতে আপনার স্ক্রিন লক ব্যবহার করতে চান?"</string>
<string name="passkey" msgid="632353688396759522">"পাসকী"</string>
<string name="password" msgid="6738570945182936667">"পাসওয়ার্ড"</string>
<string name="passkeys" msgid="5733880786866559847">"পাসকী"</string>
diff --git a/packages/CredentialManager/res/values-bs/strings.xml b/packages/CredentialManager/res/values-bs/strings.xml
index f6e6d811..6d3a676 100644
--- a/packages/CredentialManager/res/values-bs/strings.xml
+++ b/packages/CredentialManager/res/values-bs/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Kreirati pristupni ključ da se prijavite u aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Sačuvati lozinku da se prijavite u aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Sačuvati podatke za prijavu u aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Koristiti zaključavanje ekrana da kreirate pristupni ključ za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Koristiti zaključavanje ekrana da kreirate lozinku za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Koristiti zaključavanje ekrana da sačuvate podatke za prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"pristupni ključ"</string>
<string name="password" msgid="6738570945182936667">"lozinka"</string>
<string name="passkeys" msgid="5733880786866559847">"pristupni ključevi"</string>
diff --git a/packages/CredentialManager/res/values-ca/strings.xml b/packages/CredentialManager/res/values-ca/strings.xml
index 3809d92..28762e7 100644
--- a/packages/CredentialManager/res/values-ca/strings.xml
+++ b/packages/CredentialManager/res/values-ca/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Vols crear una clau d\'accés per iniciar la sessió a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Vols desar la contrasenya per iniciar la sessió a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Vols desar la informació d\'inici de sessió per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Vols fer servir el bloqueig de pantalla per crear una clau d\'accés per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Vols fer servir el bloqueig de pantalla per crear una contrasenya per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Vols fer servir el bloqueig de pantalla per desar la informació d\'inici de sessió de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"clau d\'accés"</string>
<string name="password" msgid="6738570945182936667">"contrasenya"</string>
<string name="passkeys" msgid="5733880786866559847">"claus d\'accés"</string>
diff --git a/packages/CredentialManager/res/values-cs/strings.xml b/packages/CredentialManager/res/values-cs/strings.xml
index 6e6857c..a3ff390 100644
--- a/packages/CredentialManager/res/values-cs/strings.xml
+++ b/packages/CredentialManager/res/values-cs/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Vytvořit přístupový klíč k přihlašování do aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Uložit heslo k přihlašování do aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Uložit přihlašovací údaje pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Chcete pomocí zámku obrazovky vytvořit přístupový klíč pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Chcete pomocí zámku obrazovky vytvořit heslo pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Chcete pomocí zámku obrazovky uložit přihlašovací údaje pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"přístupový klíč"</string>
<string name="password" msgid="6738570945182936667">"heslo"</string>
<string name="passkeys" msgid="5733880786866559847">"přístupové klíče"</string>
diff --git a/packages/CredentialManager/res/values-da/strings.xml b/packages/CredentialManager/res/values-da/strings.xml
index ae7c3cf..b61b81c 100644
--- a/packages/CredentialManager/res/values-da/strings.xml
+++ b/packages/CredentialManager/res/values-da/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Vil du oprette en adgangsnøgle for at logge ind på <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Vil du gemme adgangskoden for at logge ind på <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Vil du gemme loginoplysningerne til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Vil du bruge din skærmlås til at oprette en adgangsnøgle til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Vil du bruge din skærmlås til at oprette en adgangskode til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Vil du bruge din skærmlås til at gemme loginoplysningerne til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"adgangsnøgle"</string>
<string name="password" msgid="6738570945182936667">"adgangskode"</string>
<string name="passkeys" msgid="5733880786866559847">"adgangsnøgler"</string>
diff --git a/packages/CredentialManager/res/values-de/strings.xml b/packages/CredentialManager/res/values-de/strings.xml
index 4fa669b..9897443 100644
--- a/packages/CredentialManager/res/values-de/strings.xml
+++ b/packages/CredentialManager/res/values-de/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Passkey zur Anmeldung in <xliff:g id="APP_NAME">%1$s</xliff:g> erstellen?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Passwort zur Anmeldung in <xliff:g id="APP_NAME">%1$s</xliff:g> speichern?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Anmeldedaten für <xliff:g id="APP_NAME">%1$s</xliff:g> speichern?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Displaysperre verwenden, um einen Passkey für <xliff:g id="APP_NAME">%1$s</xliff:g> zu erstellen?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Displaysperre verwenden, um ein Passwort für <xliff:g id="APP_NAME">%1$s</xliff:g> zu erstellen?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Displaysperre verwenden, um Anmeldedaten für <xliff:g id="APP_NAME">%1$s</xliff:g> zu speichern?"</string>
<string name="passkey" msgid="632353688396759522">"Passkey"</string>
<string name="password" msgid="6738570945182936667">"Passwort"</string>
<string name="passkeys" msgid="5733880786866559847">"Passkeys"</string>
diff --git a/packages/CredentialManager/res/values-el/strings.xml b/packages/CredentialManager/res/values-el/strings.xml
index b6b2728..b1c3506 100644
--- a/packages/CredentialManager/res/values-el/strings.xml
+++ b/packages/CredentialManager/res/values-el/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Δημιουργία κλειδιού πρόσβασης για σύνδεση στην εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Αποθήκευση κωδικού πρόσβασης για σύνδεση στην εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Αποθήκευση πληροφοριών σύνδεσης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Θέλετε να χρησιμοποιήσετε το κλείδωμα οθόνης για τη δημιουργία ενός κλειδιού πρόσβασης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Θέλετε να χρησιμοποιήσετε το κλείδωμα οθόνης για τη δημιουργία ενός κωδικού πρόσβασης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Θέλετε να χρησιμοποιήσετε το κλείδωμα οθόνης για την αποθήκευση πληροφοριών σύνδεσης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
<string name="passkey" msgid="632353688396759522">"κλειδί πρόσβασης"</string>
<string name="password" msgid="6738570945182936667">"κωδικός πρόσβασης"</string>
<string name="passkeys" msgid="5733880786866559847">"κλειδιά πρόσβασης"</string>
diff --git a/packages/CredentialManager/res/values-en-rAU/strings.xml b/packages/CredentialManager/res/values-en-rAU/strings.xml
index f177cf9..1afd5f6 100644
--- a/packages/CredentialManager/res/values-en-rAU/strings.xml
+++ b/packages/CredentialManager/res/values-en-rAU/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Create passkey to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Save password to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Use your screen lock to create a passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Use your screen lock to create a password for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Use your screen lock to save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
<string name="passkeys" msgid="5733880786866559847">"passkeys"</string>
diff --git a/packages/CredentialManager/res/values-en-rCA/strings.xml b/packages/CredentialManager/res/values-en-rCA/strings.xml
index df4cd86..3fb3d55 100644
--- a/packages/CredentialManager/res/values-en-rCA/strings.xml
+++ b/packages/CredentialManager/res/values-en-rCA/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Create passkey to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Save password to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Use your screen lock to create a passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Use your screen lock to create a password for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Use your screen lock to save sign in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
<string name="passkeys" msgid="5733880786866559847">"passkeys"</string>
diff --git a/packages/CredentialManager/res/values-en-rGB/strings.xml b/packages/CredentialManager/res/values-en-rGB/strings.xml
index f177cf9..1afd5f6 100644
--- a/packages/CredentialManager/res/values-en-rGB/strings.xml
+++ b/packages/CredentialManager/res/values-en-rGB/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Create passkey to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Save password to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Use your screen lock to create a passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Use your screen lock to create a password for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Use your screen lock to save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
<string name="passkeys" msgid="5733880786866559847">"passkeys"</string>
diff --git a/packages/CredentialManager/res/values-en-rIN/strings.xml b/packages/CredentialManager/res/values-en-rIN/strings.xml
index f177cf9..1afd5f6 100644
--- a/packages/CredentialManager/res/values-en-rIN/strings.xml
+++ b/packages/CredentialManager/res/values-en-rIN/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Create passkey to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Save password to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Use your screen lock to create a passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Use your screen lock to create a password for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Use your screen lock to save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
<string name="passkeys" msgid="5733880786866559847">"passkeys"</string>
diff --git a/packages/CredentialManager/res/values-en-rXC/strings.xml b/packages/CredentialManager/res/values-en-rXC/strings.xml
index 77ae53a..b642c87 100644
--- a/packages/CredentialManager/res/values-en-rXC/strings.xml
+++ b/packages/CredentialManager/res/values-en-rXC/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Create passkey to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Save password to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Use your screen lock to create a passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Use your screen lock to create a password for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Use your screen lock to save sign in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
<string name="passkeys" msgid="5733880786866559847">"passkeys"</string>
diff --git a/packages/CredentialManager/res/values-es-rUS/strings.xml b/packages/CredentialManager/res/values-es-rUS/strings.xml
index 6ccfec3..e007ab7 100644
--- a/packages/CredentialManager/res/values-es-rUS/strings.xml
+++ b/packages/CredentialManager/res/values-es-rUS/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"¿Quieres crear una llave de acceso para acceder a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"¿Quieres guardar la contraseña para acceder a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"¿Quieres guardar la información de acceso para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"¿Quieres usar el bloqueo de pantalla para crear una llave de acceso para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"¿Quieres usar el bloqueo de pantalla para crear una contraseña para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"¿Quieres usar el bloqueo de pantalla para guardar la información de acceso para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"llave de acceso"</string>
<string name="password" msgid="6738570945182936667">"contraseña"</string>
<string name="passkeys" msgid="5733880786866559847">"llaves de acceso"</string>
diff --git a/packages/CredentialManager/res/values-es/strings.xml b/packages/CredentialManager/res/values-es/strings.xml
index b2c1c6f..e82f331 100644
--- a/packages/CredentialManager/res/values-es/strings.xml
+++ b/packages/CredentialManager/res/values-es/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"¿Crear llave de acceso para iniciar sesión en <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"¿Guardar contraseña para iniciar sesión en <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"¿Guardar la información de inicio de sesión de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"¿Usar tu bloqueo de pantalla para crear una llave de acceso para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"¿Usar tu bloqueo de pantalla para crear una contraseña para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"¿Usar tu bloqueo de pantalla para guardar tu información de inicio de sesión para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"llave de acceso"</string>
<string name="password" msgid="6738570945182936667">"contraseña"</string>
<string name="passkeys" msgid="5733880786866559847">"llaves de acceso"</string>
diff --git a/packages/CredentialManager/res/values-et/strings.xml b/packages/CredentialManager/res/values-et/strings.xml
index 823a016..a4c3438 100644
--- a/packages/CredentialManager/res/values-et/strings.xml
+++ b/packages/CredentialManager/res/values-et/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Kas luua rakendusse <xliff:g id="APP_NAME">%1$s</xliff:g> sisselogimiseks pääsuvõti?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Kas salvestada rakendusse <xliff:g id="APP_NAME">%1$s</xliff:g> sisselogimiseks parool?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Kas salvestada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks sisselogimisteave?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Kas kasutada ekraanilukku, et luua rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks pääsuvõti?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Kas kasutada ekraanilukku, et luua rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks parool?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Kas kasutada ekraanilukku, et salvestada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks sisselogimisteave?"</string>
<string name="passkey" msgid="632353688396759522">"pääsuvõti"</string>
<string name="password" msgid="6738570945182936667">"parool"</string>
<string name="passkeys" msgid="5733880786866559847">"pääsuvõtmed"</string>
diff --git a/packages/CredentialManager/res/values-eu/strings.xml b/packages/CredentialManager/res/values-eu/strings.xml
index 8507b3f..2f62ba3 100644
--- a/packages/CredentialManager/res/values-eu/strings.xml
+++ b/packages/CredentialManager/res/values-eu/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioan saioa hasteko sarbide-gako bat sortu nahi duzu?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioan saioa hasteko pasahitza gorde nahi duzu?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioan saioa hasteko informazioa gorde nahi duzu?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Pantailaren blokeoa erabili nahi duzu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako sarbide-gako bat sortzeko?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Pantailaren blokeoa erabili nahi duzu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako pasahitz bat sortzeko?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Pantailaren blokeoa erabili nahi duzu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioan saioa hasteko informazioa gordetzeko?"</string>
<string name="passkey" msgid="632353688396759522">"sarbide-gakoa"</string>
<string name="password" msgid="6738570945182936667">"pasahitza"</string>
<string name="passkeys" msgid="5733880786866559847">"sarbide-gakoak"</string>
@@ -75,7 +78,7 @@
<string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioan saioa hasteko aukerak desblokeatu nahi dituzu?"</string>
<string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"Aukeratu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako gordetako sarbide-gakoa"</string>
<string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"Aukeratu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako gordetako pasahitza"</string>
- <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"Aukeratu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako gordetako kredentzialak"</string>
+ <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"Aukeratu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako gordetako saioa hasteko moduak"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"Aukeratu <xliff:g id="APP_NAME">%1$s</xliff:g> zerbitzuan saioa hasteko kredentzialak"</string>
<string name="get_dialog_title_choose_option_for" msgid="4976380044745029107">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako aukera bat hautatu nahi duzu?"</string>
<string name="get_dialog_title_use_info_on" msgid="8863708099535435146">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioan erabili nahi duzu informazio hori?"</string>
@@ -89,10 +92,10 @@
<string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Desblokeatzeko, sakatu hau"</string>
<string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Ez dago saioa hasteko informaziorik"</string>
<string name="no_sign_in_info_in" msgid="2641118151920288356">"Ez dago saioa hasteko informaziorik <xliff:g id="SOURCE">%1$s</xliff:g> kontuan"</string>
- <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Kudeatu kredentzialak"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Kudeatu saioa hasteko moduak"</string>
<string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Beste gailu batean gordetakoak"</string>
<string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Erabili beste gailu bat"</string>
<string name="request_cancelled_by" msgid="3735222326886267820">"Utzi du bertan behera eskaera <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak"</string>
- <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"Saioa hasteko aukerak"</string>
+ <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"Saioa hasteko moduak"</string>
<string name="more_options_content_description" msgid="1323427365788198808">"Gehiago"</string>
</resources>
diff --git a/packages/CredentialManager/res/values-fa/strings.xml b/packages/CredentialManager/res/values-fa/strings.xml
index 656c789..6266ed2 100644
--- a/packages/CredentialManager/res/values-fa/strings.xml
+++ b/packages/CredentialManager/res/values-fa/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"برای ورود به سیستم <xliff:g id="APP_NAME">%1$s</xliff:g>، گذرکلید ایجاد شود؟"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"برای ورود به سیستم <xliff:g id="APP_NAME">%1$s</xliff:g>، گذرواژه ذخیره شود؟"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"اطلاعات ورود به سیستم <xliff:g id="APP_NAME">%1$s</xliff:g> ذخیره شود؟"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"برای ایجاد گذرکلید برای <xliff:g id="APP_NAME">%1$s</xliff:g> از قفل صفحه استفاده شود؟"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"برای ایجاد گذرواژه برای <xliff:g id="APP_NAME">%1$s</xliff:g> از قفل صفحه استفاده شود؟"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"برای ذخیرهسازی اطلاعات ورود به سیستم <xliff:g id="APP_NAME">%1$s</xliff:g> از قفل صفحه استفاده شود؟"</string>
<string name="passkey" msgid="632353688396759522">"گذرکلید"</string>
<string name="password" msgid="6738570945182936667">"گذرواژه"</string>
<string name="passkeys" msgid="5733880786866559847">"گذرکلیدها"</string>
diff --git a/packages/CredentialManager/res/values-fi/strings.xml b/packages/CredentialManager/res/values-fi/strings.xml
index f2f1bfb..838d6d9 100644
--- a/packages/CredentialManager/res/values-fi/strings.xml
+++ b/packages/CredentialManager/res/values-fi/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Luodaanko avainkoodi sisäänkirjautumista (<xliff:g id="APP_NAME">%1$s</xliff:g>) varten?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Tallennetaanko salasana sisäänkirjautumista (<xliff:g id="APP_NAME">%1$s</xliff:g>) varten?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Tallennetaanko kirjautumistiedot (<xliff:g id="APP_NAME">%1$s</xliff:g>)?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Käytetäänkö näytön lukitusta aivankoodin luomiseen sovellukselle (<xliff:g id="APP_NAME">%1$s</xliff:g>)?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Käytetäänkö näytön lukitusta salasanan luomiseen sovellukselle (<xliff:g id="APP_NAME">%1$s</xliff:g>)?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Käytetäänkö näytön lukitusta sovelluksen (<xliff:g id="APP_NAME">%1$s</xliff:g>) sisäänkirjautumistietojen tallentamiseen?"</string>
<string name="passkey" msgid="632353688396759522">"avainkoodi"</string>
<string name="password" msgid="6738570945182936667">"salasana"</string>
<string name="passkeys" msgid="5733880786866559847">"avainkoodit"</string>
diff --git a/packages/CredentialManager/res/values-fr-rCA/strings.xml b/packages/CredentialManager/res/values-fr-rCA/strings.xml
index 891e987..834dffe 100644
--- a/packages/CredentialManager/res/values-fr-rCA/strings.xml
+++ b/packages/CredentialManager/res/values-fr-rCA/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Créer une clé d\'accès pour se connecter à <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Enregistrer un mot de passe pour se connecter à <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Enregistrer les renseignements de connexion pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Utiliser le Verrouillage de l\'écran pour créer une clé d\'accès pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Utiliser le Verrouillage de l\'écran pour créer un mot de passe pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Utiliser le Verrouillage de l\'écran pour enregistrer les renseignements de connexion pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"clé d\'accès"</string>
<string name="password" msgid="6738570945182936667">"mot de passe"</string>
<string name="passkeys" msgid="5733880786866559847">"clés d\'accès"</string>
diff --git a/packages/CredentialManager/res/values-fr/strings.xml b/packages/CredentialManager/res/values-fr/strings.xml
index f2ca1fc..1c86c9b 100644
--- a/packages/CredentialManager/res/values-fr/strings.xml
+++ b/packages/CredentialManager/res/values-fr/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Créer une clé d\'accès pour se connecter à <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Enregistrer un mot de passe pour se connecter à <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Enregistrer les informations de connexion pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Utiliser le verrouillage de l\'écran afin de créer une clé d\'accès pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Utiliser le verrouillage de l\'écran afin de créer un mot de passe pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Utiliser le verrouillage de l\'écran afin d\'enregistrer les informations de connexion pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
<string name="passkey" msgid="632353688396759522">"clé d\'accès"</string>
<string name="password" msgid="6738570945182936667">"mot de passe"</string>
<string name="passkeys" msgid="5733880786866559847">"clés d\'accès"</string>
@@ -75,7 +78,7 @@
<string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"Déverrouiller les options de connexion pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
<string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"Choisir une clé d\'accès enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"Choisir un mot de passe enregistré pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"Choisir des informations de connexion enregistrées pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"Choisissez les identifiants à utiliser pour la connexion à <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"Choisir des infos de connexion pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="get_dialog_title_choose_option_for" msgid="4976380044745029107">"Choisir une option pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
<string name="get_dialog_title_use_info_on" msgid="8863708099535435146">"Utiliser ces informations dans <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
diff --git a/packages/CredentialManager/res/values-gl/strings.xml b/packages/CredentialManager/res/values-gl/strings.xml
index 7117e1c..a95f6b9 100644
--- a/packages/CredentialManager/res/values-gl/strings.xml
+++ b/packages/CredentialManager/res/values-gl/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Queres crear unha clave de acceso para iniciar sesión en <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Queres gardar o contrasinal para iniciar sesión en <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Queres gardar a información de inicio de sesión de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Queres usar o bloqueo de pantalla para crear unha clave de acceso para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Queres usar o bloqueo de pantalla para crear un contrasinal para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Queres usar o bloqueo de pantalla para gardar a información de inicio de sesión para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"clave de acceso"</string>
<string name="password" msgid="6738570945182936667">"contrasinal"</string>
<string name="passkeys" msgid="5733880786866559847">"claves de acceso"</string>
@@ -75,7 +78,7 @@
<string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"Queres desbloquear as opcións de inicio de sesión para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"Escolle unha clave de acceso gardada para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"Escolle un contrasinal gardado para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"Escolle un método de inicio de sesión gardado para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"Escolle un inicio de sesión gardado para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"Escolle un inicio de sesión para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="get_dialog_title_choose_option_for" msgid="4976380044745029107">"Queres escoller unha opción para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_info_on" msgid="8863708099535435146">"Queres usar esta información en <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-gu/strings.xml b/packages/CredentialManager/res/values-gu/strings.xml
index 98b4686..ed08128 100644
--- a/packages/CredentialManager/res/values-gu/strings.xml
+++ b/packages/CredentialManager/res/values-gu/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g>માં સાઇન ઇન કરવા માટે પાસકી બનાવીએ?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g>માં સાઇન ઇન કરવા માટે પાસવર્ડ સાચવીએ?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે સાઇન-ઇન કરવાની માહિતી સાચવીએ?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે કોઈ પાસકી બનાવવા માટે, શું તમારા સ્ક્રીન લૉકનો ઉપયોગ કરીએ?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે કોઈ પાસવર્ડ બનાવવા માટે, શું તમારા સ્ક્રીન લૉકનો ઉપયોગ કરીએ?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે સાઇન ઇનની માહિતી સાચવવા માટે, શું તમારા સ્ક્રીન લૉકનો ઉપયોગ કરીએ?"</string>
<string name="passkey" msgid="632353688396759522">"પાસકી"</string>
<string name="password" msgid="6738570945182936667">"પાસવર્ડ"</string>
<string name="passkeys" msgid="5733880786866559847">"પાસકી"</string>
diff --git a/packages/CredentialManager/res/values-hi/strings.xml b/packages/CredentialManager/res/values-hi/strings.xml
index 9bc5feb..16f04c5 100644
--- a/packages/CredentialManager/res/values-hi/strings.xml
+++ b/packages/CredentialManager/res/values-hi/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"क्या आपको <xliff:g id="APP_NAME">%1$s</xliff:g> में साइन इन करने के लिए पासकी बनानी है?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"क्या आपको <xliff:g id="APP_NAME">%1$s</xliff:g> में साइन इन करने के लिए पासवर्ड सेव करना है?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"क्या आपको <xliff:g id="APP_NAME">%1$s</xliff:g> के लिए साइन-इन की जानकारी सेव करनी है?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"क्या स्क्रीन लॉक का इस्तेमाल करके, <xliff:g id="APP_NAME">%1$s</xliff:g> के लिए पासकी बनानी है?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"क्या स्क्रीन लॉक का इस्तेमाल करके, <xliff:g id="APP_NAME">%1$s</xliff:g> के लिए पासवर्ड बनाना है?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"क्या स्क्रीन लॉक का इस्तेमाल करके, <xliff:g id="APP_NAME">%1$s</xliff:g> में साइन इन करने की जानकारी सेव करनी है?"</string>
<string name="passkey" msgid="632353688396759522">"पासकी"</string>
<string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
<string name="passkeys" msgid="5733880786866559847">"पासकी"</string>
@@ -75,24 +78,24 @@
<string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"क्या आपको <xliff:g id="APP_NAME">%1$s</xliff:g> में साइन इन करने के विकल्पों को अनलॉक करना है?"</string>
<string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सेव की गई पासकी चुनें"</string>
<string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सेव किया गया पासवर्ड चुनें"</string>
- <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सेव किया गया साइन इन क्रेडेंशियल चुनें"</string>
+ <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सेव किया गया क्रेडेंशियल चुनें"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="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="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>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"साइन-इन करने के लिए विकल्प"</string>
<string name="button_label_view_more" msgid="3429098227286495651">"ज़्यादा देखें"</string>
<string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> के लिए"</string>
<string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"लॉक किए गए पासवर्ड मैनेजर"</string>
<string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"अनलॉक करने के लिए टैप करें"</string>
<string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"साइन-इन करने से जुड़ी कोई जानकारी उपलब्ध नहीं है"</string>
<string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> में साइन-इन की जानकारी नहीं है"</string>
- <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"साइन इन करने की सुविधा को मैनेज करें"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"साइन-इन किए गए खाते मैनेज करें"</string>
<string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"किसी दूसरे डिवाइस से"</string>
<string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"दूसरे डिवाइस का इस्तेमाल करें"</string>
<string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> की ओर से अनुरोध रद्द किया गया"</string>
- <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"साइन-इन करने के विकल्प"</string>
+ <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"साइन-इन करने के लिए विकल्प"</string>
<string name="more_options_content_description" msgid="1323427365788198808">"ज़्यादा"</string>
</resources>
diff --git a/packages/CredentialManager/res/values-hr/strings.xml b/packages/CredentialManager/res/values-hr/strings.xml
index 968a747..2c10c21 100644
--- a/packages/CredentialManager/res/values-hr/strings.xml
+++ b/packages/CredentialManager/res/values-hr/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Želite li izraditi pristupni ključ za prijavu u aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Želite li spremiti zaporku za prijavu u aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Želite li spremiti informacije o prijavi za <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Želite li upotrijebiti zaključavanje zaslona za izradu pristupnog ključa za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Želite li upotrijebiti zaključavanje zaslona za izradu zaporke za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Želite li upotrijebiti zaključavanje zaslona za spremanje podataka za prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"pristupni ključ"</string>
<string name="password" msgid="6738570945182936667">"zaporka"</string>
<string name="passkeys" msgid="5733880786866559847">"pristupni ključevi"</string>
diff --git a/packages/CredentialManager/res/values-hu/strings.xml b/packages/CredentialManager/res/values-hu/strings.xml
index e601da6..46b80da 100644
--- a/packages/CredentialManager/res/values-hu/strings.xml
+++ b/packages/CredentialManager/res/values-hu/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Létrehoz azonosítókulcsot a következőbe való bejelentkezéshez: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Menti a jelszót a következőbe való bejelentkezéshez: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Menti a bejelentkezési adatokat a következőhöz: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Képernyőzár használatával hoz létre azonosítókulcsot a következőhöz: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Képernyőzár használatával hoz létre jelszót a következőhöz: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Képernyőzár használatával menti a bejelentkezési adatokat a következőhöz: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"azonosítókulcs"</string>
<string name="password" msgid="6738570945182936667">"jelszó"</string>
<string name="passkeys" msgid="5733880786866559847">"azonosítókulcsait"</string>
@@ -75,7 +78,7 @@
<string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"Feloldja a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> bejelentkezési lehetőségeit?"</string>
<string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"Mentett azonosítókulcs kiválasztása a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz"</string>
<string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"Mentett jelszó kiválasztása a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz"</string>
- <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"Mentett bejelentkezési adatok kiválasztása a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz"</string>
+ <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"Mentett bejelentkezési adatok kiválasztása – <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"Válasszon bejelentkezési adatokat – <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="get_dialog_title_choose_option_for" msgid="4976380044745029107">"Kiválaszt egy lehetőséget a következőbe való bejelentkezéshez: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_info_on" msgid="8863708099535435146">"Használni szeretná ezt az információt a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazásban?"</string>
diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml
index 79a2624..2a8784c 100644
--- a/packages/CredentialManager/res/values-hy/strings.xml
+++ b/packages/CredentialManager/res/values-hy/strings.xml
@@ -27,7 +27,7 @@
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Անցաբառերի հետ ավելի ապահով է"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Անցաբառերի շնորհիվ դուք բարդ գաղտնաբառեր ստեղծելու կամ հիշելու անհրաժեշտություն չեք ունենա"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Անցաբառերը գաղտնագրված թվային բանալիներ են, որոնք ստեղծվում են մատնահետքի, դեմքով ապակողպման կամ էկրանի կողպման օգտագործմամբ"</string>
- <string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Դուք կարող եք մուտք գործել այլ սարքերում, քանի որ անցաբառերը պահվում են գաղտնաբառերի կառավարիչում"</string>
+ <string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Դուք կարող եք մուտք գործել այլ սարքերում, քանի որ մուտքի բանալիները պահվում են գաղտնաբառերի կառավարիչում"</string>
<string name="more_about_passkeys_title" msgid="7797903098728837795">"Ավելին՝ անցաբառերի մասին"</string>
<string name="passwordless_technology_title" msgid="2497513482056606668">"Գաղտնաբառեր չպահանջող տեխնոլոգիա"</string>
<string name="passwordless_technology_detail" msgid="6853928846532955882">"Անցաբառերը ձեզ թույլ են տալիս մուտք գործել առանց գաղտնաբառերի։ Ձեզ պարզապես հարկավոր է օգտագործել ձեր մատնահետքը, դիմաճանաչումը, PIN կոդը կամ նախշը՝ ձեր ինքնությունը հաստատելու և անցաբառ ստեղծելու համար։"</string>
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Ստեղծե՞լ անցաբառ՝ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելված մուտք գործելու համար"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Պահե՞լ գաղտնաբառը՝ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելված մուտք գործելու համար"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Պահե՞լ «<xliff:g id="APP_NAME">%1$s</xliff:g>» հավելվածի մուտքի տվյալները"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Օգտագործե՞լ էկրանի կողպումը՝ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի մուտքի բանալի ստեղծելու համար"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Օգտագործե՞լ էկրանի կողպումը՝ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի գաղտնաբառ ստեղծելու համար"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Օգտագործե՞լ էկրանի կողպումը՝ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի մուտքի տվյալները պահելու համար"</string>
<string name="passkey" msgid="632353688396759522">"անցաբառ"</string>
<string name="password" msgid="6738570945182936667">"գաղտնաբառ"</string>
<string name="passkeys" msgid="5733880786866559847">"անցաբառեր"</string>
@@ -53,7 +56,7 @@
<string name="save_password_on_other_device_title" msgid="5829084591948321207">"Պահե՞լ գաղտնաբառն այլ սարքում"</string>
<string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Պահե՞լ մուտքի տվյալներն այլ սարքում"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Միշտ մուտք գործե՞լ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> հավելվածի միջոցով"</string>
- <string name="use_provider_for_all_description" msgid="1998772715863958997">"Այս գաղտնաբառերի կառավարչում <xliff:g id="USERNAME">%1$s</xliff:g> օգտատերը կկարողանա պահել իր գաղտնաբառերն ու անցաբառերը, որպեսզի հետագայում ավելի արագ մուտք գործի հաշիվ"</string>
+ <string name="use_provider_for_all_description" msgid="1998772715863958997">"Այս գաղտնաբառերի կառավարչում <xliff:g id="USERNAME">%1$s</xliff:g> օգտատերը կկարողանա պահել իր գաղտնաբառերն ու մուտքի բանալիները, որպեսզի հետագայում ավելի արագ մուտք գործի հաշիվ"</string>
<string name="set_as_default" msgid="4415328591568654603">"Նշել որպես կանխադրված"</string>
<string name="settings" msgid="6536394145760913145">"Կարգավորումներ"</string>
<string name="use_once" msgid="9027366575315399714">"Օգտագործել մեկ անգամ"</string>
diff --git a/packages/CredentialManager/res/values-in/strings.xml b/packages/CredentialManager/res/values-in/strings.xml
index e7556b0..3eac6e9 100644
--- a/packages/CredentialManager/res/values-in/strings.xml
+++ b/packages/CredentialManager/res/values-in/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Buat kunci sandi untuk login ke <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Simpan sandi untuk login ke <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Simpan info login untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Gunakan kunci layar Anda untuk membuat kunci sandi untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Gunakan kunci layar Anda untuk membuat sandi untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Gunakan kunci layar Anda untuk menyimpan info login untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"kunci sandi"</string>
<string name="password" msgid="6738570945182936667">"sandi"</string>
<string name="passkeys" msgid="5733880786866559847">"kunci sandi"</string>
diff --git a/packages/CredentialManager/res/values-is/strings.xml b/packages/CredentialManager/res/values-is/strings.xml
index 970a2e6..9fd552b 100644
--- a/packages/CredentialManager/res/values-is/strings.xml
+++ b/packages/CredentialManager/res/values-is/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Búa til aðgangslykil til að skrá þig inn á <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Vista aðgangsorð til að skrá þig inn á <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Viltu vista innskráningarupplýsingar fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Nota skjálásinn til að búa til aðgangslykil fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Nota skjálásinn til að búa til aðgangsorð fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Nota skjálásinn til að vista innskráningarupplýsingar fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"aðgangslykill"</string>
<string name="password" msgid="6738570945182936667">"aðgangsorð"</string>
<string name="passkeys" msgid="5733880786866559847">"aðgangslykla"</string>
diff --git a/packages/CredentialManager/res/values-it/strings.xml b/packages/CredentialManager/res/values-it/strings.xml
index a04a840..0d6f839 100644
--- a/packages/CredentialManager/res/values-it/strings.xml
+++ b/packages/CredentialManager/res/values-it/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Creare passkey per accedere all\'app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Salvare password per accedere all\'app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Vuoi salvare i dati di accesso di <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Usare il blocco schermo per creare una passkey per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Usare il blocco schermo per creare una password per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Usare il blocco schermo per salvare le informazioni di accesso per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
<string name="passkeys" msgid="5733880786866559847">"passkey"</string>
diff --git a/packages/CredentialManager/res/values-iw/strings.xml b/packages/CredentialManager/res/values-iw/strings.xml
index 87dee5f..7d24825 100644
--- a/packages/CredentialManager/res/values-iw/strings.xml
+++ b/packages/CredentialManager/res/values-iw/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"ליצור מפתח גישה כדי להיכנס לחשבון ב-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"לשמור את הסיסמה כדי להיכנס לחשבון ב-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"לשמור את פרטי הכניסה של <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"להשתמש בנעילת המסך כדי ליצור מפתח גישה בשביל <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"להשתמש בנעילת המסך כדי ליצור סיסמה בשביל <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"להשתמש בנעילת המסך כדי לשמור את פרטי הכניסה בשביל <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"מפתח גישה"</string>
<string name="password" msgid="6738570945182936667">"סיסמה"</string>
<string name="passkeys" msgid="5733880786866559847">"מפתחות גישה"</string>
diff --git a/packages/CredentialManager/res/values-ja/strings.xml b/packages/CredentialManager/res/values-ja/strings.xml
index 71746dc..16e3195e3 100644
--- a/packages/CredentialManager/res/values-ja/strings.xml
+++ b/packages/CredentialManager/res/values-ja/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> にログインするためにパスキーを作成しますか?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> にログインするためにパスワードを保存しますか?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> のログイン情報を保存しますか?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"画面ロックを使用して <xliff:g id="APP_NAME">%1$s</xliff:g> のパスキーを作成しますか?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"画面ロックを使用して <xliff:g id="APP_NAME">%1$s</xliff:g> のパスワードを作成しますか?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"画面ロックを使用して <xliff:g id="APP_NAME">%1$s</xliff:g> のログイン情報を保存しますか?"</string>
<string name="passkey" msgid="632353688396759522">"パスキー"</string>
<string name="password" msgid="6738570945182936667">"パスワード"</string>
<string name="passkeys" msgid="5733880786866559847">"パスキー"</string>
diff --git a/packages/CredentialManager/res/values-ka/strings.xml b/packages/CredentialManager/res/values-ka/strings.xml
index 51f1332..a852f1c 100644
--- a/packages/CredentialManager/res/values-ka/strings.xml
+++ b/packages/CredentialManager/res/values-ka/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"შესასვლელად წვდომის გასაღების შექმნა: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"შესასვლელი პაროლის შენახვა: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"აპში შესვლის ინფორმაციის შენახვა: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"გსურთ თქვენი ეკრანის დაბლოკვის გამოყენება <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის წვდომის გასაღების შესაქმნელად?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"გსურთ თქვენი ეკრანის დაბლოკვის გამოყენება <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის პაროლის შესაქმნელად?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"გსურთ თქვენი ეკრანის დაბლოკვის გამოყენება შესვლის ინფორმაციის შესანახად <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის?"</string>
<string name="passkey" msgid="632353688396759522">"წვდომის გასაღები"</string>
<string name="password" msgid="6738570945182936667">"პაროლი"</string>
<string name="passkeys" msgid="5733880786866559847">"წვდომის გასაღები"</string>
diff --git a/packages/CredentialManager/res/values-kk/strings.xml b/packages/CredentialManager/res/values-kk/strings.xml
index 7393ca0..03eb9d2 100644
--- a/packages/CredentialManager/res/values-kk/strings.xml
+++ b/packages/CredentialManager/res/values-kk/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасына кіру үшін кіру кілті жасалсын ба?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасына кіру үшін құпия сөз сақталсын ба?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін кіру мәліметін сақтау керек пе?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> кіру кілтін жасау үшін экран құлпын пайдаланасыз ба?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> құпия сөзін жасау үшін экран құлпын пайдаланасыз ба?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасындағы аккаунтқа кіру ақпаратын сақтау үшін экран құлпын пайдаланасыз ба?"</string>
<string name="passkey" msgid="632353688396759522">"Кіру кілті"</string>
<string name="password" msgid="6738570945182936667">"құпия сөз"</string>
<string name="passkeys" msgid="5733880786866559847">"кіру кілттері"</string>
@@ -75,7 +78,7 @@
<string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін кіру опциялары ашылсын ба?"</string>
<string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін сақталған кіру кілтін таңдаңыз"</string>
<string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін сақталған құпия сөзді таңдаңыз"</string>
- <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін сақталған тіркелу деректерін таңдаңыз"</string>
+ <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін сақталған кіру деректерін таңдаңыз"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="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>
diff --git a/packages/CredentialManager/res/values-km/strings.xml b/packages/CredentialManager/res/values-km/strings.xml
index ac0d427..279474f 100644
--- a/packages/CredentialManager/res/values-km/strings.xml
+++ b/packages/CredentialManager/res/values-km/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"បង្កើតកូដសម្ងាត់ ដើម្បីចូលគណនី <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"រក្សាទុកពាក្យសម្ងាត់ ដើម្បីចូលគណនី <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"រក្សាទុកព័ត៌មានចូលគណនីសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"ប្រើមុខងារចាក់សោអេក្រង់របស់អ្នក ដើម្បីបង្កើតកូដសម្ងាត់សម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"ប្រើមុខងារចាក់សោអេក្រង់របស់អ្នក ដើម្បីបង្កើតពាក្យសម្ងាត់សម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"ប្រើមុខងារចាក់សោអេក្រង់របស់អ្នក ដើម្បីរក្សាទុកព័ត៌មានចូលគណនីសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
<string name="passkey" msgid="632353688396759522">"កូដសម្ងាត់"</string>
<string name="password" msgid="6738570945182936667">"ពាក្យសម្ងាត់"</string>
<string name="passkeys" msgid="5733880786866559847">"កូដសម្ងាត់"</string>
diff --git a/packages/CredentialManager/res/values-kn/strings.xml b/packages/CredentialManager/res/values-kn/strings.xml
index 031fa65..2f090e4 100644
--- a/packages/CredentialManager/res/values-kn/strings.xml
+++ b/packages/CredentialManager/res/values-kn/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಲು ಪಾಸ್ಕೀ ಯನ್ನು ರಚಿಸಬೇಕೇ?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಲು ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ಸೇವ್ ಮಾಡಬೇಕೇ?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಸೈನ್-ಇನ್ ಮಾಹಿತಿಯನ್ನು ಸೇವ್ ಮಾಡಬೇಕೇ?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಪಾಸ್ಕೀಯನ್ನು ರಚಿಸಲು ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸುವುದೇ?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ರಚಿಸಲು ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸುವುದೇ?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಸೈನ್ ಇನ್ ಮಾಹಿತಿಯನ್ನು ಸೇವ್ ಮಾಡಲು ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸುವುದೇ?"</string>
<string name="passkey" msgid="632353688396759522">"ಪಾಸ್ಕೀ"</string>
<string name="password" msgid="6738570945182936667">"ಪಾಸ್ವರ್ಡ್"</string>
<string name="passkeys" msgid="5733880786866559847">"ಪಾಸ್ಕೀಗಳು"</string>
diff --git a/packages/CredentialManager/res/values-ko/strings.xml b/packages/CredentialManager/res/values-ko/strings.xml
index e29ae68..f2ead85 100644
--- a/packages/CredentialManager/res/values-ko/strings.xml
+++ b/packages/CredentialManager/res/values-ko/strings.xml
@@ -20,7 +20,7 @@
<string name="app_name" msgid="4539824758261855508">"인증서 관리자"</string>
<string name="string_cancel" msgid="6369133483981306063">"취소"</string>
<string name="string_continue" msgid="1346732695941131882">"계속"</string>
- <string name="string_more_options" msgid="2763852250269945472">"다른 방법 저장하기"</string>
+ <string name="string_more_options" msgid="2763852250269945472">"다른 방법으로 저장하기"</string>
<string name="string_learn_more" msgid="4541600451688392447">"자세히 알아보기"</string>
<string name="content_description_show_password" msgid="3283502010388521607">"비밀번호 표시"</string>
<string name="content_description_hide_password" msgid="6841375971631767996">"비밀번호 숨기기"</string>
@@ -39,9 +39,12 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"비밀번호 없는 미래로 나아가는 과정에서 비밀번호는 여전히 패스키와 함께 사용될 것입니다."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> 저장 위치 선택"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"정보를 저장해서 다음에 더 빠르게 로그인하려면 비밀번호 관리자를 선택하세요."</string>
- <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"패스키를 생성하여 <xliff:g id="APP_NAME">%1$s</xliff:g>에 로그인하시겠습니까?"</string>
+ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g>의 패스키를 생성할까요?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"비밀번호를 저장하여 <xliff:g id="APP_NAME">%1$s</xliff:g>에 로그인하시겠습니까?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g>의 로그인 정보를 저장하시겠습니까?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g>용 패스키를 생성하기 위해 화면 잠금을 사용하시겠습니까?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g>용 패스키를 생성하기 위해 비밀번호를 사용하시겠습니까?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g>용 로그인 정보를 저장하기 위해 화면 잠금을 사용하시겠습니까?"</string>
<string name="passkey" msgid="632353688396759522">"패스키"</string>
<string name="password" msgid="6738570945182936667">"비밀번호"</string>
<string name="passkeys" msgid="5733880786866559847">"패스키"</string>
diff --git a/packages/CredentialManager/res/values-ky/strings.xml b/packages/CredentialManager/res/values-ky/strings.xml
index 5e48ae5..4e3fed1 100644
--- a/packages/CredentialManager/res/values-ky/strings.xml
+++ b/packages/CredentialManager/res/values-ky/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосуна кирүү үчүн киргизүүчү ачкычты түзөсүзбү?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосуна кирүү үчүн сырсөздү сактайсызбы?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн кирүү маалыматы сакталсынбы?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу үчүн киргизүүчү ачкыч катары экрандын кулпусун колдоносузбу?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу үчүн сырсөз катары экрандын кулпусун колдоносузбу?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу үчүн кирүү маалыматы катары экрандын кулпусун колдоносузбу?"</string>
<string name="passkey" msgid="632353688396759522">"киргизүүчү ачкыч"</string>
<string name="password" msgid="6738570945182936667">"сырсөз"</string>
<string name="passkeys" msgid="5733880786866559847">"киргизүүчү ачкычтар"</string>
diff --git a/packages/CredentialManager/res/values-lo/strings.xml b/packages/CredentialManager/res/values-lo/strings.xml
index c3733a3..e0f8839 100644
--- a/packages/CredentialManager/res/values-lo/strings.xml
+++ b/packages/CredentialManager/res/values-lo/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"ສ້າງກະແຈຜ່ານເພື່ອເຂົ້າສູ່ລະບົບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"ບັນທຶກລະຫັດຜ່ານເພື່ອເຂົ້າສູ່ລະບົບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"ບັນທຶກຂໍ້ມູນການເຂົ້າສູ່ລະບົບສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"ໃຊ້ລັອກໜ້າຈໍຂອງທ່ານເພື່ອສ້າງກະແຈຜ່ານສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"ໃຊ້ລັອກໜ້າຈໍຂອງທ່ານເພື່ອສ້າງລະຫັດຜ່ານສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"ໃຊ້ລັອກໜ້າຈໍຂອງທ່ານເພື່ອບັນທຶກຂໍ້ມູນການເຂົ້າສູ່ລະບົບສຳລັບ<xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
<string name="passkey" msgid="632353688396759522">"ກະແຈຜ່ານ"</string>
<string name="password" msgid="6738570945182936667">"ລະຫັດຜ່ານ"</string>
<string name="passkeys" msgid="5733880786866559847">"ກະແຈຜ່ານ"</string>
diff --git a/packages/CredentialManager/res/values-lt/strings.xml b/packages/CredentialManager/res/values-lt/strings.xml
index 453a0e0..5e0fbe0 100644
--- a/packages/CredentialManager/res/values-lt/strings.xml
+++ b/packages/CredentialManager/res/values-lt/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Sukurti prieigos raktą, skirtą prisijungti prie „<xliff:g id="APP_NAME">%1$s</xliff:g>“?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Sukurti slaptažodį, skirtą prisijungti prie „<xliff:g id="APP_NAME">%1$s</xliff:g>“?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Išsaugoti prisijungimo prie „<xliff:g id="APP_NAME">%1$s</xliff:g>“ informaciją?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Sukurti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prieigos raktą naudojant ekrano užraktą?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Sukurti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ slaptažodį naudojant ekrano užraktą?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Naudoti ekrano užraktą norint išsaugoti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prisijungimo informaciją?"</string>
<string name="passkey" msgid="632353688396759522">"„passkey“"</string>
<string name="password" msgid="6738570945182936667">"slaptažodis"</string>
<string name="passkeys" msgid="5733880786866559847">"prieigos raktas"</string>
diff --git a/packages/CredentialManager/res/values-lv/strings.xml b/packages/CredentialManager/res/values-lv/strings.xml
index 4da1051..f56d7f1 100644
--- a/packages/CredentialManager/res/values-lv/strings.xml
+++ b/packages/CredentialManager/res/values-lv/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Vai izveidot piekļuves atslēgu, lai pierakstītos lietotnē <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Vai saglabāt paroli, lai pierakstītos lietotnē <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Vai saglabāt pierakstīšanās informāciju lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Vai izmantot ekrāna bloķēšanas opciju, lai izveidotu piekļuves atslēgu lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Vai izmantot ekrāna bloķēšanas opciju, lai izveidotu paroli lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Vai izmantot ekrāna bloķēšanas opciju, lai saglabātu pierakstīšanās informāciju lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"piekļuves atslēga"</string>
<string name="password" msgid="6738570945182936667">"parole"</string>
<string name="passkeys" msgid="5733880786866559847">"piekļuves atslēgas"</string>
@@ -93,6 +96,6 @@
<string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"No citas ierīces"</string>
<string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Izmantot citu ierīci"</string>
<string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> atcēla pieprasījumu"</string>
- <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"Pierakstīšanās iespējas"</string>
+ <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"Pierakstīšanās opcijas"</string>
<string name="more_options_content_description" msgid="1323427365788198808">"Vairāk"</string>
</resources>
diff --git a/packages/CredentialManager/res/values-mk/strings.xml b/packages/CredentialManager/res/values-mk/strings.xml
index 95554a2..ec4df56 100644
--- a/packages/CredentialManager/res/values-mk/strings.xml
+++ b/packages/CredentialManager/res/values-mk/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Да се создаде криптографски клуч за најавување на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Да се зачува лозинката за најавување на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Да се зачуваат податоците за најавување за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Да се користи заклучувањето екран за создавање криптографски клуч за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Да се користи заклучувањето екран за создавање лозинка за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Да се користи заклучувањето екран за зачувување на податоците за најавување за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"криптографски клуч"</string>
<string name="password" msgid="6738570945182936667">"лозинка"</string>
<string name="passkeys" msgid="5733880786866559847">"криптографски клучеви"</string>
diff --git a/packages/CredentialManager/res/values-ml/strings.xml b/packages/CredentialManager/res/values-ml/strings.xml
index eccfb51..f7d74fe 100644
--- a/packages/CredentialManager/res/values-ml/strings.xml
+++ b/packages/CredentialManager/res/values-ml/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിലേക്ക് സൈൻ ഇൻ ചെയ്യാൻ പാസ്കീ സൃഷ്ടിക്കണോ?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിലേക്ക് സൈൻ ഇൻ ചെയ്യാൻ പാസ്വേഡ് സംരക്ഷിക്കണോ?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി സൈൻ ഇൻ വിവരങ്ങൾ സംരക്ഷിക്കണോ?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി പാസ്കീ സൃഷ്ടിക്കാൻ നിങ്ങളുടെ സ്ക്രീൻ ലോക്ക് ഉപയോഗിക്കണോ?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി പാസ്വേഡ് സൃഷ്ടിക്കാൻ നിങ്ങളുടെ സ്ക്രീൻ ലോക്ക് ഉപയോഗിക്കണോ?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി സൈൻ ഇൻ വിവരങ്ങൾ സംരക്ഷിക്കാൻ നിങ്ങളുടെ സ്ക്രീൻ ലോക്ക് ഉപയോഗിക്കണോ?"</string>
<string name="passkey" msgid="632353688396759522">"പാസ്കീ"</string>
<string name="password" msgid="6738570945182936667">"പാസ്വേഡ്"</string>
<string name="passkeys" msgid="5733880786866559847">"പാസ്കീകൾ"</string>
diff --git a/packages/CredentialManager/res/values-mn/strings.xml b/packages/CredentialManager/res/values-mn/strings.xml
index 5b1cbe9..093baab 100644
--- a/packages/CredentialManager/res/values-mn/strings.xml
+++ b/packages/CredentialManager/res/values-mn/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д нэвтрэхийн тулд нэвтрэх түлхүүр үүсгэх үү?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д нэвтрэхийн тулд нууц үгийг хадгалах уу?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н нэвтрэх мэдээллийг хадгалах уу?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д нэвтрэх түлхүүр үүсгэхийн тулд дэлгэцийн түгжээгээ ашиглах уу?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д нууц үг үүсгэхийн тулд дэлгэцийн түгжээгээ ашиглах уу?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д нэвтрэх мэдээлэл хадгалахын тулд дэлгэцийн түгжээгээ ашиглах уу?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"нууц үг"</string>
<string name="passkeys" msgid="5733880786866559847">"нэвтрэх түлхүүрүүд"</string>
diff --git a/packages/CredentialManager/res/values-mr/strings.xml b/packages/CredentialManager/res/values-mr/strings.xml
index a0a4a7d..cc52617 100644
--- a/packages/CredentialManager/res/values-mr/strings.xml
+++ b/packages/CredentialManager/res/values-mr/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> मध्ये साइन इन करण्यासाठी पासकी तयार करायची आहे का?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> मध्ये साइन इन करण्यासाठी पासवर्ड सेव्ह करायचा आहे का?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी साइन-इनसंबंधित माहिती सेव्ह करायची का?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी पासकी तयार करण्याकरिता तुमचे स्क्रीन लॉक वापरायचे आहे का?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी पासवर्ड तयार करण्याकरिता तुमचे स्क्रीन लॉक वापरायचे आहे का?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी साइन-इनसंबंधित माहिती सेव्ह करण्याकरिता तुमचे स्क्रीन लॉक वापरायचे आहे का?"</string>
<string name="passkey" msgid="632353688396759522">"पासकी"</string>
<string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
<string name="passkeys" msgid="5733880786866559847">"पासकी"</string>
@@ -82,7 +85,7 @@
<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>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"साइन-इन पर्याय"</string>
<string name="button_label_view_more" msgid="3429098227286495651">"आणखी पहा"</string>
<string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> साठी"</string>
<string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"लॉक केलेले पासवर्ड व्यवस्थापक"</string>
@@ -93,6 +96,6 @@
<string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"दुसऱ्या डिव्हाइस वरून"</string>
<string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"वेगळे डिव्हाइस वापरा"</string>
<string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> ने विनंती रद्द केली आहे"</string>
- <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"साइन इन करण्याचे पर्याय"</string>
+ <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"साइन-इन पर्याय"</string>
<string name="more_options_content_description" msgid="1323427365788198808">"आणखी"</string>
</resources>
diff --git a/packages/CredentialManager/res/values-ms/strings.xml b/packages/CredentialManager/res/values-ms/strings.xml
index c866013..62a7deb 100644
--- a/packages/CredentialManager/res/values-ms/strings.xml
+++ b/packages/CredentialManager/res/values-ms/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Buat kunci laluan untuk log masuk ke <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Simpan kata laluan untuk log masuk ke <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Simpan maklumat log masuk untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Gunakan kunci skrin anda untuk membuat kunci laluan bagi <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Gunakan kunci skrin anda untuk membuat kata laluan bagi <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Gunakan kunci skrin anda untuk menyimpan maklumat log masuk bagi <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"kunci laluan"</string>
<string name="password" msgid="6738570945182936667">"kata laluan"</string>
<string name="passkeys" msgid="5733880786866559847">"kunci laluan"</string>
diff --git a/packages/CredentialManager/res/values-my/strings.xml b/packages/CredentialManager/res/values-my/strings.xml
index a66e7b6..aa08aa7 100644
--- a/packages/CredentialManager/res/values-my/strings.xml
+++ b/packages/CredentialManager/res/values-my/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> သို့ လက်မှတ်ထိုးဝင်ရန် လျှို့ဝှက်ကီး ပြုလုပ်မလား။"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> သို့ လက်မှတ်ထိုးဝင်ရန် စကားဝှက်ကို သိမ်းမလား။"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် လက်မှတ်ထိုးဝင်ရန် အချက်အလက်ကို သိမ်းမလား။"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် လျှို့ဝှက်ကီးပြုလုပ်ရန် သင့်ဖန်သားပြင်လော့ခ်ကို သုံးမလား။"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် စကားဝှက်ပြုလုပ်ရန် သင့်ဖန်သားပြင်လော့ခ်ကို သုံးမလား။"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် လက်မှတ်ထိုးဝင်ရန် အချက်အလက်များ သိမ်းရန် သင့်ဖန်သားပြင်လော့ခ်ကို သုံးမလား။"</string>
<string name="passkey" msgid="632353688396759522">"လျှို့ဝှက်ကီး"</string>
<string name="password" msgid="6738570945182936667">"စကားဝှက်"</string>
<string name="passkeys" msgid="5733880786866559847">"လျှို့ဝှက်ကီးများ"</string>
diff --git a/packages/CredentialManager/res/values-nb/strings.xml b/packages/CredentialManager/res/values-nb/strings.xml
index 7f4fa6b..8cf3444 100644
--- a/packages/CredentialManager/res/values-nb/strings.xml
+++ b/packages/CredentialManager/res/values-nb/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Vil du opprette en passnøkkel for å logge på <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Vil du lagre passordet for å logge på <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Vil du lagre påloggingsinformasjon for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Vil du bruke skjermlåsen til å opprette en passnøkkel for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Vil du bruke skjermlåsen til å opprette et passord for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Vil du bruke skjermlåsen til å lagre påloggingsinformasjon for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passnøkkel"</string>
<string name="password" msgid="6738570945182936667">"passord"</string>
<string name="passkeys" msgid="5733880786866559847">"passnøkler"</string>
diff --git a/packages/CredentialManager/res/values-ne/strings.xml b/packages/CredentialManager/res/values-ne/strings.xml
index 6e69476..161a16d 100644
--- a/packages/CredentialManager/res/values-ne/strings.xml
+++ b/packages/CredentialManager/res/values-ne/strings.xml
@@ -22,8 +22,8 @@
<string name="string_continue" msgid="1346732695941131882">"जारी राख्नुहोस्"</string>
<string name="string_more_options" msgid="2763852250269945472">"अर्को तरिकाले सेभ गर्नुहोस्"</string>
<string name="string_learn_more" msgid="4541600451688392447">"थप जान्नुहोस्"</string>
- <string name="content_description_show_password" msgid="3283502010388521607">"पासवर्ड देखाइयोस्"</string>
- <string name="content_description_hide_password" msgid="6841375971631767996">"पासवर्ड लुकाइयोस्"</string>
+ <string name="content_description_show_password" msgid="3283502010388521607">"पासवर्ड देखाउनुहोस्"</string>
+ <string name="content_description_hide_password" msgid="6841375971631767996">"पासवर्ड लुकाउनुहोस्"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"पासकीका सहायताले सुरक्षित रहनुहोस्"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"तपाईंले पासकी बनाउनुभयो भने तपाईंले जटिल पासवर्ड बनाउनु वा तिनलाई याद गरिराख्नु पर्दैन"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"पासकी भनेको तपाईंले आफ्नो फिंगरप्रिन्ट, अनुहार वा स्क्रिन लक प्रयोग गरेर बनाएको इन्क्रिप्ट गरिएको डिजिटल की हो"</string>
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> मा साइन इन गर्न पासकी बनाउने हो?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> मा साइन इन गर्न पासवर्ड सेभ गर्ने हो?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> मा साइन गर्न प्रयोग गरिने जानकारी सेभ गर्ने हो?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> मा पासकी बनाउन आफ्नो स्क्रिन लक प्रयोग गर्ने हो?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> मा पासवर्ड राख्न आफ्नो स्क्रिन लक प्रयोग गर्ने हो?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> मा साइन इनसम्बन्धी जानकारी सेभ गर्न आफ्नो स्क्रिन लक प्रयोग गर्ने हो?"</string>
<string name="passkey" msgid="632353688396759522">"पासकी"</string>
<string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
<string name="passkeys" msgid="5733880786866559847">"पासकीहरू"</string>
diff --git a/packages/CredentialManager/res/values-nl/strings.xml b/packages/CredentialManager/res/values-nl/strings.xml
index 50eefc7..6707ff0 100644
--- a/packages/CredentialManager/res/values-nl/strings.xml
+++ b/packages/CredentialManager/res/values-nl/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Toegangssleutel maken om in te loggen bij <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Wachtwoord opslaan om in te loggen bij <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Inloggegevens opslaan voor <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Je schermvergrendeling gebruiken om een toegangssleutel voor <xliff:g id="APP_NAME">%1$s</xliff:g> te maken?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Je schermvergrendeling gebruiken om een wachtwoord voor <xliff:g id="APP_NAME">%1$s</xliff:g> te maken?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Je schermvergrendeling gebruiken om inloggegevens voor <xliff:g id="APP_NAME">%1$s</xliff:g> op te slaan?"</string>
<string name="passkey" msgid="632353688396759522">"Toegangssleutel"</string>
<string name="password" msgid="6738570945182936667">"wachtwoord"</string>
<string name="passkeys" msgid="5733880786866559847">"toegangssleutels"</string>
@@ -82,7 +85,7 @@
<string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Op een andere manier inloggen"</string>
<string name="snackbar_action" msgid="37373514216505085">"Opties bekijken"</string>
<string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Doorgaan"</string>
- <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opties voor inloggen"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Inlogopties"</string>
<string name="button_label_view_more" msgid="3429098227286495651">"Meer bekijken"</string>
<string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Voor <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
<string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Vergrendelde wachtwoordmanagers"</string>
@@ -93,6 +96,6 @@
<string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Via een ander apparaat"</string>
<string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Een ander apparaat gebruiken"</string>
<string name="request_cancelled_by" msgid="3735222326886267820">"Verzoek geannuleerd door <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"Opties voor inloggen"</string>
+ <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"Inlogopties"</string>
<string name="more_options_content_description" msgid="1323427365788198808">"Meer"</string>
</resources>
diff --git a/packages/CredentialManager/res/values-or/strings.xml b/packages/CredentialManager/res/values-or/strings.xml
index 56e585d..9781c49 100644
--- a/packages/CredentialManager/res/values-or/strings.xml
+++ b/packages/CredentialManager/res/values-or/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g>ରେ ସାଇନ ଇନ କରିବାକୁ ପାସକୀ ତିଆରି କରିବେ?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g>ରେ ସାଇନ ଇନ କରିବାକୁ ପାସୱାର୍ଡ ସେଭ କରିବେ?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ସାଇନ-ଇନର ସୂଚନା ସେଭ କରିବେ?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ଏକ ପାସକୀ ତିଆରି କରିବାକୁ ଆପଣଙ୍କ ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରିବେ?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ଏକ ପାସୱାର୍ଡ ତିଆରି କରିବାକୁ ଆପଣଙ୍କ ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରିବେ?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ସାଇନ ଇନ ସୂଚନା ସେଭ କରିବାକୁ ଆପଣଙ୍କ ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରିବେ?"</string>
<string name="passkey" msgid="632353688396759522">"ପାସକୀ"</string>
<string name="password" msgid="6738570945182936667">"ପାସୱାର୍ଡ"</string>
<string name="passkeys" msgid="5733880786866559847">"ପାସକୀଗୁଡ଼ିକ"</string>
diff --git a/packages/CredentialManager/res/values-pa/strings.xml b/packages/CredentialManager/res/values-pa/strings.xml
index d23d209..a5aceb7 100644
--- a/packages/CredentialManager/res/values-pa/strings.xml
+++ b/packages/CredentialManager/res/values-pa/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਵਿੱਚ ਸਾਈਨ-ਇਨ ਕਰਨ ਲਈ ਪਾਸਕੀ ਬਣਾਉਣੀ ਹੈ?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਵਿੱਚ ਸਾਈਨ-ਇਨ ਕਰਨ ਲਈ ਪਾਸਵਰਡ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਰੱਖਿਅਤ ਕਰਨੀ ਹੈ?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਪਾਸਕੀ ਬਣਾਉਣ ਵਾਸਤੇ ਆਪਣੇ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਪਾਸਵਰਡ ਬਣਾਉਣ ਵਾਸਤੇ ਆਪਣੇ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਰੱਖਿਅਤ ਕਰਨ ਵਾਸਤੇ ਆਪਣੇ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
<string name="passkey" msgid="632353688396759522">"ਪਾਸਕੀ"</string>
<string name="password" msgid="6738570945182936667">"ਪਾਸਵਰਡ"</string>
<string name="passkeys" msgid="5733880786866559847">"ਪਾਸਕੀਆਂ"</string>
diff --git a/packages/CredentialManager/res/values-pl/strings.xml b/packages/CredentialManager/res/values-pl/strings.xml
index 5fa26b4..eca8699 100644
--- a/packages/CredentialManager/res/values-pl/strings.xml
+++ b/packages/CredentialManager/res/values-pl/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Utworzyć klucz dostępu do logowania w aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Zapisać hasło do logowania w aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Zapisać dane używane do logowania w aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Użyć metody odblokowania ekranu do utworzenia klucza dostępu do aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Użyć metody odblokowania ekranu do utworzenia hasła do aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Użyć metody odblokowania ekranu do zapisania danych logowania do aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"klucz"</string>
<string name="password" msgid="6738570945182936667">"hasło"</string>
<string name="passkeys" msgid="5733880786866559847">"klucze dostępu"</string>
diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml
index 994bc55..b508af9 100644
--- a/packages/CredentialManager/res/values-pt-rBR/strings.xml
+++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Criar chave de acesso para fazer login no app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Salvar senha para fazer login no app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Salvar informações de login do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Usar o bloqueio de tela para criar uma chave de acesso para o app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Usar o bloqueio de tela para criar uma senha para o app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Usar o bloqueio de tela para salvar as informações de login do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"chave de acesso"</string>
<string name="password" msgid="6738570945182936667">"senha"</string>
<string name="passkeys" msgid="5733880786866559847">"chaves de acesso"</string>
diff --git a/packages/CredentialManager/res/values-pt-rPT/strings.xml b/packages/CredentialManager/res/values-pt-rPT/strings.xml
index 26c6491..8b6b2b2 100644
--- a/packages/CredentialManager/res/values-pt-rPT/strings.xml
+++ b/packages/CredentialManager/res/values-pt-rPT/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Criar a chave de acesso para iniciar sessão na app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Guardar a palavra-passe para iniciar sessão na app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Guardar as informações de início de sessão da app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Usar o bloqueio de ecrã para criar uma chave de acesso para a app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Usar o bloqueio de ecrã para criar uma palavra-passe para a app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Usar o bloqueio de ecrã para guardar as informações de início de sessão para a app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"chave de acesso"</string>
<string name="password" msgid="6738570945182936667">"palavra-passe"</string>
<string name="passkeys" msgid="5733880786866559847">"chaves de acesso"</string>
diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml
index 994bc55..b508af9 100644
--- a/packages/CredentialManager/res/values-pt/strings.xml
+++ b/packages/CredentialManager/res/values-pt/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Criar chave de acesso para fazer login no app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Salvar senha para fazer login no app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Salvar informações de login do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Usar o bloqueio de tela para criar uma chave de acesso para o app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Usar o bloqueio de tela para criar uma senha para o app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Usar o bloqueio de tela para salvar as informações de login do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"chave de acesso"</string>
<string name="password" msgid="6738570945182936667">"senha"</string>
<string name="passkeys" msgid="5733880786866559847">"chaves de acesso"</string>
diff --git a/packages/CredentialManager/res/values-ro/strings.xml b/packages/CredentialManager/res/values-ro/strings.xml
index 342b6ab..ccbf228 100644
--- a/packages/CredentialManager/res/values-ro/strings.xml
+++ b/packages/CredentialManager/res/values-ro/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Creezi o cheie de acces pentru a te conecta la <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Salvezi parola pentru a te conecta la <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Salvezi informațiile de conectare pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Folosești blocarea ecranului ca să creezi o cheie de acces pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Folosești blocarea ecranului ca să creezi o parolă pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Folosești blocarea ecranului ca să salvezi informațiile de conectare pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"cheia de acces"</string>
<string name="password" msgid="6738570945182936667">"parolă"</string>
<string name="passkeys" msgid="5733880786866559847">"cheile de acces"</string>
@@ -89,7 +92,7 @@
<string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Atinge pentru a debloca"</string>
<string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Fără informații de conectare"</string>
<string name="no_sign_in_info_in" msgid="2641118151920288356">"Nu există informații de conectare în contul <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
- <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestionează acreditările"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestionează conectările"</string>
<string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De pe alt dispozitiv"</string>
<string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Folosește alt dispozitiv"</string>
<string name="request_cancelled_by" msgid="3735222326886267820">"Solicitare anulată de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-ru/strings.xml b/packages/CredentialManager/res/values-ru/strings.xml
index e472695..c9c8dcc 100644
--- a/packages/CredentialManager/res/values-ru/strings.xml
+++ b/packages/CredentialManager/res/values-ru/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Создать ключ доступа для входа в приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Сохранить пароль для входа в приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Сохранить данные для входа в приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Использовать способ разблокировки экрана, чтобы создать ключ доступа для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Использовать способ разблокировки экрана, чтобы создать пароль для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Использовать способ разблокировки экрана, чтобы сохранить данные для входа в приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
<string name="passkey" msgid="632353688396759522">"ключ доступа"</string>
<string name="password" msgid="6738570945182936667">"пароль"</string>
<string name="passkeys" msgid="5733880786866559847">"ключи доступа"</string>
diff --git a/packages/CredentialManager/res/values-si/strings.xml b/packages/CredentialManager/res/values-si/strings.xml
index ce0b9cd..0acc655 100644
--- a/packages/CredentialManager/res/values-si/strings.xml
+++ b/packages/CredentialManager/res/values-si/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> වෙත පුරනය වීමට මුරයතුරක් තනන්න ද?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> වෙත පුරනය වීමට මුරපදය සුරකින්න ද?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා පුරනය වීමේ තතු සුරකින්න ද?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා මුරපදයක් තැනීමට ඔබේ තිර අගුල භාවිත කරන්න ද?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා මුරපදයක් තැනීමට ඔබේ තිර අගුල භාවිත කරන්න ද?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා පුරනය වීමේ තතු සුරැකීමට ඔබේ තිර අගුල භාවිතා කරන්න ද?"</string>
<string name="passkey" msgid="632353688396759522">"මුරයතුර"</string>
<string name="password" msgid="6738570945182936667">"මුරපදය"</string>
<string name="passkeys" msgid="5733880786866559847">"මුරයතුරු"</string>
diff --git a/packages/CredentialManager/res/values-sk/strings.xml b/packages/CredentialManager/res/values-sk/strings.xml
index 62a2e69..c2626ea 100644
--- a/packages/CredentialManager/res/values-sk/strings.xml
+++ b/packages/CredentialManager/res/values-sk/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Chcete vytvoriť prístupový kľúč na prihlasovanie do aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Chcete uložiť heslo na prihlasovanie do aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Chcete uložiť prihlasovacie údaje pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Chcete pomocou zámky obrazovky vytvoriť prístupový kľúč pre <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Chcete pomocou zámky obrazovky vytvoriť heslo pre <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Chcete pomocou zámky obrazovky uložiť prihlasovacie údaje pre <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"prístupový kľúč"</string>
<string name="password" msgid="6738570945182936667">"heslo"</string>
<string name="passkeys" msgid="5733880786866559847">"prístupové kľúče"</string>
diff --git a/packages/CredentialManager/res/values-sl/strings.xml b/packages/CredentialManager/res/values-sl/strings.xml
index d2aaf68..79c8c72 100644
--- a/packages/CredentialManager/res/values-sl/strings.xml
+++ b/packages/CredentialManager/res/values-sl/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Želite ustvariti ključ za dostop za prijavo v aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Želite shraniti geslo za prijavo v aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Želite shraniti podatke za prijavo za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Želite uporabiti zaklepanje zaslona za ustvarjanje ključa za dostop za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Želite uporabiti zaklepanje zaslona za ustvarjanje gesla za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Želite uporabiti zaklepanje zaslona za shranjevanje podatkov za prijavo za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"ključ za dostop"</string>
<string name="password" msgid="6738570945182936667">"geslo"</string>
<string name="passkeys" msgid="5733880786866559847">"ključi za dostop"</string>
diff --git a/packages/CredentialManager/res/values-sq/strings.xml b/packages/CredentialManager/res/values-sq/strings.xml
index f31d16a..722bee6 100644
--- a/packages/CredentialManager/res/values-sq/strings.xml
+++ b/packages/CredentialManager/res/values-sq/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Të krijohet një çelës kalimi për t\'u identifikuar në <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Të ruhet fjalëkalimi për t\'u identifikuar në <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Të ruhen informacionet e identifikimit për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Të përdoret kyçja e ekranit për të krijuar një çelës kalimi për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Të përdoret kyçja e ekranit për të krijuar një fjalëkalim për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Të përdoret kyçja e ekranit për të ruajtur informacionet e identifikimit për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"çelësin e kalimit"</string>
<string name="password" msgid="6738570945182936667">"fjalëkalimi"</string>
<string name="passkeys" msgid="5733880786866559847">"çelësat e kalimit"</string>
@@ -89,7 +92,7 @@
<string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Trokit për të shkyçur"</string>
<string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Nuk ka informacione për identifikimin"</string>
<string name="no_sign_in_info_in" msgid="2641118151920288356">"Nuk ka informacione regjistrimi në <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
- <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Identifikimet e menaxhimit"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Menaxho identifikimet"</string>
<string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Nga një pajisje tjetër"</string>
<string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Përdor një pajisje tjetër"</string>
<string name="request_cancelled_by" msgid="3735222326886267820">"Kërkesa u anulua nga <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-sr/strings.xml b/packages/CredentialManager/res/values-sr/strings.xml
index e68a20c..58110aa 100644
--- a/packages/CredentialManager/res/values-sr/strings.xml
+++ b/packages/CredentialManager/res/values-sr/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Желите да направите приступни кључ да бисте се пријавили у <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Желите да сачувате лозинку да бисте се пријавили у <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Желите да сачувате податке за пријављивање за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Желите да користите откључавање екрана да бисте направили приступни кључ за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Желите да користите откључавање екрана да бисте направили лозинку за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Желите да користите откључавање екрана да бисте сачували податке за пријављивање за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"приступни кôд"</string>
<string name="password" msgid="6738570945182936667">"лозинка"</string>
<string name="passkeys" msgid="5733880786866559847">"приступни кодови"</string>
diff --git a/packages/CredentialManager/res/values-sv/strings.xml b/packages/CredentialManager/res/values-sv/strings.xml
index e18fea1..331b124 100644
--- a/packages/CredentialManager/res/values-sv/strings.xml
+++ b/packages/CredentialManager/res/values-sv/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Vill du skapa en nyckel för att logga in i <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Vill du spara lösenordet för att logga in i <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Vill du spara inloggningsuppgifterna för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Vill du använda skärmlåset för att skapa en nyckel för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Vill du använda skärmlåset för att skapa ett lösenord för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Vill du använda skärmlåset för att spara inloggningsuppgifter för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"nyckel"</string>
<string name="password" msgid="6738570945182936667">"lösenord"</string>
<string name="passkeys" msgid="5733880786866559847">"nycklar"</string>
diff --git a/packages/CredentialManager/res/values-sw/strings.xml b/packages/CredentialManager/res/values-sw/strings.xml
index 65672d5..888b01c 100644
--- a/packages/CredentialManager/res/values-sw/strings.xml
+++ b/packages/CredentialManager/res/values-sw/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Ungependa kubuni ufunguo wa siri wa kuingia katika akaunti ya <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Ungependa kuhifadhi nenosiri la kuingia katika akaunti ya <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Ungependa kuhifadhi maelezo ya kuingia katika akaunti ya <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Ungependa kutumia mbinu yako ya kufunga skrini kubuni ufunguo wa siri wa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Ungependa kutumia mbinu yako ya kufunga skrini kubuni nenosiri la <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Ungependa kutumia mbinu yako ya kufunga skrini kuhifadhi maelezo ya kuingia katika akaunti ya <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"ufunguo wa siri"</string>
<string name="password" msgid="6738570945182936667">"nenosiri"</string>
<string name="passkeys" msgid="5733880786866559847">"funguo za siri"</string>
diff --git a/packages/CredentialManager/res/values-ta/strings.xml b/packages/CredentialManager/res/values-ta/strings.xml
index 49d1710..ba1eb60 100644
--- a/packages/CredentialManager/res/values-ta/strings.xml
+++ b/packages/CredentialManager/res/values-ta/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸில் உள்நுழைய கடவுச்சாவியை உருவாக்கவா?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸில் உள்நுழைய கடவுச்சொல்லைச் சேமிக்கவா?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கான உள்நுழைவுத் தகவலைச் சேமிக்கவா?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கான கடவுச்சாவியை உருவாக்க உங்கள் திரைப் பூட்டைப் பயன்படுத்தவா?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கான கடவுச்சொல்லை உருவாக்க உங்கள் திரைப் பூட்டைப் பயன்படுத்தவா?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கான உள்நுழைவுத் தகவலைச் சேமிக்க உங்கள் திரைப் பூட்டைப் பயன்படுத்தவா?"</string>
<string name="passkey" msgid="632353688396759522">"கடவுச்சாவி"</string>
<string name="password" msgid="6738570945182936667">"கடவுச்சொல்"</string>
<string name="passkeys" msgid="5733880786866559847">"கடவுச்சாவிகள்"</string>
diff --git a/packages/CredentialManager/res/values-te/strings.xml b/packages/CredentialManager/res/values-te/strings.xml
index 742a422..e2e362b 100644
--- a/packages/CredentialManager/res/values-te/strings.xml
+++ b/packages/CredentialManager/res/values-te/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g>కు సైన్ ఇన్ చేయడానికి పాస్-కీని క్రియేట్ చేయాలా?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g>కు సైన్ ఇన్ చేయడానికి పాస్వర్డ్ను సేవ్ చేయాలా?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం సైన్ ఇన్ సమాచారాన్ని సేవ్ చేయాలా?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"మీ స్క్రీన్ లాక్ను ఉపయోగించి <xliff:g id="APP_NAME">%1$s</xliff:g>కు పాస్-కీని క్రియేట్ చేయాలా?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"మీ స్క్రీన్ లాక్ను ఉపయోగించి <xliff:g id="APP_NAME">%1$s</xliff:g>కు పాస్వర్డ్ను క్రియేట్ చేయాలా?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"మీ స్క్రీన్ లాక్ను ఉపయోగించి <xliff:g id="APP_NAME">%1$s</xliff:g>కు సంబంధించిన సైన్-ఇన్ సమాచారాన్ని సేవ్ చేయాలా?"</string>
<string name="passkey" msgid="632353688396759522">"పాస్-కీ"</string>
<string name="password" msgid="6738570945182936667">"పాస్వర్డ్"</string>
<string name="passkeys" msgid="5733880786866559847">"పాస్-కీలు"</string>
diff --git a/packages/CredentialManager/res/values-th/strings.xml b/packages/CredentialManager/res/values-th/strings.xml
index 3f9de26..876371a 100644
--- a/packages/CredentialManager/res/values-th/strings.xml
+++ b/packages/CredentialManager/res/values-th/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"สร้างพาสคีย์เพื่อลงชื่อเข้าใช้ <xliff:g id="APP_NAME">%1$s</xliff:g> ไหม"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"บันทึกรหัสผ่านเพื่อลงชื่อเข้าใช้ <xliff:g id="APP_NAME">%1$s</xliff:g> ไหม"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"บันทึกข้อมูลการลงชื่อเข้าใช้สำหรับ <xliff:g id="APP_NAME">%1$s</xliff:g> ไหม"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"ต้องการใช้ฟีเจอร์ล็อกหน้าจอเพื่อสร้างพาสคีย์สำหรับ <xliff:g id="APP_NAME">%1$s</xliff:g> ใช่ไหม"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"ต้องการใช้ฟีเจอร์ล็อกหน้าจอเพื่อสร้างรหัสผ่านสำหรับ <xliff:g id="APP_NAME">%1$s</xliff:g> ใช่ไหม"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"ต้องการใช้ฟีเจอร์ล็อกหน้าจอเพื่อบันทึกข้อมูลการลงชื่อเข้าใช้สำหรับ <xliff:g id="APP_NAME">%1$s</xliff:g> ใช่ไหม"</string>
<string name="passkey" msgid="632353688396759522">"พาสคีย์"</string>
<string name="password" msgid="6738570945182936667">"รหัสผ่าน"</string>
<string name="passkeys" msgid="5733880786866559847">"พาสคีย์"</string>
diff --git a/packages/CredentialManager/res/values-tl/strings.xml b/packages/CredentialManager/res/values-tl/strings.xml
index 659c1ec..163e93a 100644
--- a/packages/CredentialManager/res/values-tl/strings.xml
+++ b/packages/CredentialManager/res/values-tl/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Gumawa ng passkey para mag-sign in sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"I-save ang password para mag-sign in sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"I-save ang impormasyon sa pag-sign in para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Gamitin ang iyong lock ng screen para gumawa ng passkey para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Gamitin ang iyong lock ng screen para gumawa ng password para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Gamitin ang iyong lock ng screen para mag-save ng impormasyon sa pag-sign in para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
<string name="passkeys" msgid="5733880786866559847">"mga passkey"</string>
diff --git a/packages/CredentialManager/res/values-tr/strings.xml b/packages/CredentialManager/res/values-tr/strings.xml
index 5139a67..b11ca07 100644
--- a/packages/CredentialManager/res/values-tr/strings.xml
+++ b/packages/CredentialManager/res/values-tr/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasında oturum açmak için geçiş anahtarı oluşturulsun mu?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasında oturum açmak için şifre kaydedilsin mi?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> için oturum açma bilgileri kaydedilsin mi?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> geçiş anahtarı oluşturmak için ekran kilidiniz kullanılsın mı?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> şifresi oluşturmak için ekran kilidiniz kullanılsın mı?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> oturum açma bilgilerini kaydetmek için ekran kilidiniz kullanılsın mı?"</string>
<string name="passkey" msgid="632353688396759522">"Geçiş anahtarı"</string>
<string name="password" msgid="6738570945182936667">"Şifre"</string>
<string name="passkeys" msgid="5733880786866559847">"Geçiş anahtarlarınızın"</string>
diff --git a/packages/CredentialManager/res/values-uk/strings.xml b/packages/CredentialManager/res/values-uk/strings.xml
index 62eac9a..cbc67d9 100644
--- a/packages/CredentialManager/res/values-uk/strings.xml
+++ b/packages/CredentialManager/res/values-uk/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Створити ключ доступу для входу в додаток <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Зберегти пароль для входу в додаток <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Зберегти дані для входу для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Використати спосіб розблокування екрана, щоб створити ключ доступу для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Використати спосіб розблокування екрана, щоб створити пароль для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Використати спосіб розблокування екрана, щоб зберегти дані для входу в додаток <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"ключ доступу"</string>
<string name="password" msgid="6738570945182936667">"пароль"</string>
<string name="passkeys" msgid="5733880786866559847">"ключі доступу"</string>
diff --git a/packages/CredentialManager/res/values-ur/strings.xml b/packages/CredentialManager/res/values-ur/strings.xml
index 9fad273..67cf20a 100644
--- a/packages/CredentialManager/res/values-ur/strings.xml
+++ b/packages/CredentialManager/res/values-ur/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> میں سائن ان کرنے کیلئے پاس کی تخلیق کریں؟"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> میں سائن ان کرنے کیلئے پاس ورڈ محفوظ کریں؟"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے سائن ان کی معلومات محفوظ کریں؟"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے پاس کی بنانے کے لیے اپنا اسکرین لاک استعمال کریں؟"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> کا پاس ورڈ بنانے کے لیے اپنا اسکرین لاک استعمال کریں؟"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> کی سائن ان کی معلومات محفوظ کرنے کے لیے اپنا اسکرین لاک استعمال کریں؟"</string>
<string name="passkey" msgid="632353688396759522">"پاس کی"</string>
<string name="password" msgid="6738570945182936667">"پاس ورڈ"</string>
<string name="passkeys" msgid="5733880786866559847">"پاس کیز"</string>
diff --git a/packages/CredentialManager/res/values-uz/strings.xml b/packages/CredentialManager/res/values-uz/strings.xml
index a3d2025..ae7f06e 100644
--- a/packages/CredentialManager/res/values-uz/strings.xml
+++ b/packages/CredentialManager/res/values-uz/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga kirish uchun kirish kaliti yaratilsinmi?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga kirish uchun parol saqlansinmi?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun kirish maʼlumoti saqlansinmi?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasida kirish kaliti yaratish uchun ekranni qulflashdan foydalanilsinmi?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasida parol yaratish uchun ekranni qulflashdan foydalanilsinmi?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga kirish axborotlarini saqlash uchun ekranni qulflashdan foydalanilsinmi?"</string>
<string name="passkey" msgid="632353688396759522">"kalit"</string>
<string name="password" msgid="6738570945182936667">"parol"</string>
<string name="passkeys" msgid="5733880786866559847">"kalitlar"</string>
diff --git a/packages/CredentialManager/res/values-vi/strings.xml b/packages/CredentialManager/res/values-vi/strings.xml
index da04efd..2b59857 100644
--- a/packages/CredentialManager/res/values-vi/strings.xml
+++ b/packages/CredentialManager/res/values-vi/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Tạo khoá truy cập để đăng nhập vào <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Lưu mật khẩu để đăng nhập vào <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Lưu thông tin đăng nhập cho <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Sử dụng phương thức khoá màn hình để tạo khoá truy cập cho ứng dụng <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Sử dụng phương thức khoá màn hình để tạo mật khẩu cho ứng dụng <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Sử dụng phương thức khoá màn hình để lưu thông tin đăng nhập cho ứng dụng <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"khoá đăng nhập"</string>
<string name="password" msgid="6738570945182936667">"mật khẩu"</string>
<string name="passkeys" msgid="5733880786866559847">"khoá truy cập"</string>
@@ -82,7 +85,7 @@
<string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Đăng nhập bằng cách khác"</string>
<string name="snackbar_action" msgid="37373514216505085">"Xem các lựa chọn"</string>
<string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Tiếp tục"</string>
- <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Tuỳ chọn đăng nhập"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Lựa chọn đăng nhập"</string>
<string name="button_label_view_more" msgid="3429098227286495651">"Xem thêm"</string>
<string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Cho <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
<string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Trình quản lý mật khẩu đã khoá"</string>
diff --git a/packages/CredentialManager/res/values-zh-rCN/strings.xml b/packages/CredentialManager/res/values-zh-rCN/strings.xml
index 968e978..9b7ae0d 100644
--- a/packages/CredentialManager/res/values-zh-rCN/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rCN/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"要创建通行密钥以便登录“<xliff:g id="APP_NAME">%1$s</xliff:g>”吗?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"要保存密码以便登录“<xliff:g id="APP_NAME">%1$s</xliff:g>”吗?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"要保存“<xliff:g id="APP_NAME">%1$s</xliff:g>”的登录信息吗?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"要使用屏锁为“<xliff:g id="APP_NAME">%1$s</xliff:g>”创建通行密钥?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"要使用屏锁为“<xliff:g id="APP_NAME">%1$s</xliff:g>”创建密码?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"要使用屏锁为“<xliff:g id="APP_NAME">%1$s</xliff:g>”保存登录信息?"</string>
<string name="passkey" msgid="632353688396759522">"通行密钥"</string>
<string name="password" msgid="6738570945182936667">"密码"</string>
<string name="passkeys" msgid="5733880786866559847">"通行密钥"</string>
diff --git a/packages/CredentialManager/res/values-zh-rHK/strings.xml b/packages/CredentialManager/res/values-zh-rHK/strings.xml
index 7a375c9..4ff00c3 100644
--- a/packages/CredentialManager/res/values-zh-rHK/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rHK/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"要建立密鑰以登入 <xliff:g id="APP_NAME">%1$s</xliff:g> 嗎?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"要儲存密碼以登入 <xliff:g id="APP_NAME">%1$s</xliff:g> 嗎?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"要儲存 <xliff:g id="APP_NAME">%1$s</xliff:g> 的登入資料嗎?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"要使用螢幕鎖定方式建立「<xliff:g id="APP_NAME">%1$s</xliff:g>」的密鑰嗎?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"要使用螢幕鎖定方式建立「<xliff:g id="APP_NAME">%1$s</xliff:g>」的密碼嗎?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"要使用螢幕鎖定方式儲存「<xliff:g id="APP_NAME">%1$s</xliff:g>」的登入資料嗎?"</string>
<string name="passkey" msgid="632353688396759522">"密鑰"</string>
<string name="password" msgid="6738570945182936667">"密碼"</string>
<string name="passkeys" msgid="5733880786866559847">"密鑰"</string>
diff --git a/packages/CredentialManager/res/values-zh-rTW/strings.xml b/packages/CredentialManager/res/values-zh-rTW/strings.xml
index 0299088..c8bd87d 100644
--- a/packages/CredentialManager/res/values-zh-rTW/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rTW/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"要建立用於登入「<xliff:g id="APP_NAME">%1$s</xliff:g>」的密碼金鑰嗎?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"要儲存用於登入「<xliff:g id="APP_NAME">%1$s</xliff:g>」的密碼嗎?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"要儲存「<xliff:g id="APP_NAME">%1$s</xliff:g>」的登入資訊嗎?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"要使用螢幕鎖定建立「<xliff:g id="APP_NAME">%1$s</xliff:g>」的密碼金鑰嗎?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"要使用螢幕鎖定建立「<xliff:g id="APP_NAME">%1$s</xliff:g>」的密碼嗎?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"要使用螢幕鎖定儲存「<xliff:g id="APP_NAME">%1$s</xliff:g>」的登入資訊嗎?"</string>
<string name="passkey" msgid="632353688396759522">"密碼金鑰"</string>
<string name="password" msgid="6738570945182936667">"密碼"</string>
<string name="passkeys" msgid="5733880786866559847">"密碼金鑰"</string>
diff --git a/packages/CredentialManager/res/values-zu/strings.xml b/packages/CredentialManager/res/values-zu/strings.xml
index 4f888f4..7e6300b5 100644
--- a/packages/CredentialManager/res/values-zu/strings.xml
+++ b/packages/CredentialManager/res/values-zu/strings.xml
@@ -42,6 +42,9 @@
<string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Sungula ukhiye wokudlula ukuze ungene ngemvume ku-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="4481366993598649224">"Londoloza iphasiwedi ukuze ungene ngemvume ku-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Londoloza ulwazi lokungena lwe-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Sebenzisa ukukhiya isikrini sakho ukuze usungule ukhiye wokudlula we-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Sebenzisa ukukhiya isikrini sakho ukuze usungule iphasiwedi ye-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Sebenzisa ukukhiya isikrini sakho ukuze ulondoloze ulwazi lokungena ngemvume lwe-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"ukhiye wokudlula"</string>
<string name="password" msgid="6738570945182936667">"iphasiwedi"</string>
<string name="passkeys" msgid="5733880786866559847">"okhiye bokudlula"</string>
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 9fd386f..9db681a 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -68,14 +68,6 @@
<string name="choose_create_option_password_title">Save password to sign in to <xliff:g id="app_name" example="Tribank">%1$s</xliff:g>?</string>
<!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is others. [CHAR LIMIT=200] -->
<string name="choose_create_option_sign_in_title">Save sign-in info for <xliff:g id="app_name" example="Tribank">%1$s</xliff:g>?</string>
- <!-- This appears as a description of the modal bottom sheet when the single tap sign in flow is used for the create passkey flow. [CHAR LIMIT=200] -->
- <string name="choose_create_single_tap_passkey_title">Use your screen lock to create a passkey for <xliff:g id="app_name" example="Shrine">%1$s</xliff:g>?</string>
- <!-- This appears as a description of the modal bottom sheet when the single tap sign in flow is used for the create password flow. [CHAR LIMIT=200] -->
- <string name="choose_create_single_tap_password_title">Use your screen lock to create a password for <xliff:g id="app_name" example="Shrine">%1$s</xliff:g>?</string>
- <!-- This appears as a description of the modal bottom sheet when the single tap sign in flow is used for the create flow when the credential type is others. [CHAR LIMIT=200] -->
- <!-- TODO(b/326243891) : Confirm with team on dynamically setting this based on recent product and ux discussions (does not disrupt e2e) -->
- <string name="choose_create_single_tap_sign_in_title">Use your screen lock to save sign in info for <xliff:g id="app_name" example="Shrine">%1$s</xliff:g>?</string>
- <!-- Types which are inserted as a placeholder as credentialTypes for other strings. [CHAR LIMIT=200] -->
<string name="passkey">passkey</string>
<string name="password">password</string>
<string name="passkeys">passkeys</string>
@@ -126,15 +118,21 @@
<!-- Strings for the get flow. -->
<!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved passkey to sign in to the app. [CHAR LIMIT=200] -->
- <string name="get_dialog_title_use_passkey_for">Use your saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+ <string name="get_dialog_title_use_passkey_for">Use your saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
<!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved password to sign in to the app. [CHAR LIMIT=200] -->
- <string name="get_dialog_title_use_password_for">Use your saved password for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+ <string name="get_dialog_title_use_password_for">Use your saved password for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
<!-- This appears as a description of the modal bottom sheet when the single tap sign in flow is used for the get flow. [CHAR LIMIT=200] -->
<string name="get_dialog_title_single_tap_for">Use your screen lock to sign in to <xliff:g id="app_name" example="Shrine">%1$s</xliff:g> with <xliff:g id="username" example="beckett-bakery@gmail.com">%2$s</xliff:g></string>
<!-- This appears as the title of the dialog asking for user confirmation to use the single user credential (previously saved or to be created) to sign in to the app. [CHAR LIMIT=200] -->
- <string name="get_dialog_title_use_sign_in_for">Use your sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+ <string name="get_dialog_title_use_sign_in_for">Use your account for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+ <!-- This appears as the description of the modal bottom sheet asking for user confirmation to use the biometric screen embedded within credential manager for passkey authentication. [CHAR LIMIT=200] -->
+ <string name="get_dialog_description_single_tap_passkey">Sign in to <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> with your saved passkey for <xliff:g id="username" example="beckett@gmail.com">%2$s</xliff:g>.</string>
+ <!-- This appears as the description of the modal bottom sheet asking for user confirmation to use the biometric screen embedded within credential manager for password authentication. [CHAR LIMIT=200] -->
+ <string name="get_dialog_description_single_tap_password">Sign in to <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> with your saved password for <xliff:g id="username" example="beckett@gmail.com">%2$s</xliff:g>.</string>
+ <!-- This appears as the description of the modal bottom sheet asking for user confirmation to use the biometric screen embedded within credential manager for saved sign-in authentication. [CHAR LIMIT=200] -->
+ <string name="get_dialog_description_single_tap_saved_sign_in">Sign in to <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> with your saved sign-in info for <xliff:g id="username" example="beckett@gmail.com">%2$s</xliff:g>.</string>
<!-- This appears as the title of the dialog asking for user confirmation to unlock / authenticate (e.g. via fingerprint, faceId, passcode etc.) so that we can retrieve their sign-in options. [CHAR LIMIT=200] -->
- <string name="get_dialog_title_unlock_options_for">Unlock sign-in options for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+ <string name="get_dialog_title_unlock_options_for">Unlock sign-in options for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
<!-- This appears as the title of the dialog asking for user to make a choice from multiple previously saved passkey to sign in to the app. [CHAR LIMIT=200] -->
<string name="get_dialog_title_choose_passkey_for">Choose a saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
<!-- This appears as the title of the dialog asking for user to make a choice from multiple previously saved passwords to sign in to the app. [CHAR LIMIT=200] -->
@@ -142,7 +140,7 @@
<!-- This appears as the title of the dialog asking for user to make a choice from multiple previously saved credentials to sign in to the app. [CHAR LIMIT=200] -->
<string name="get_dialog_title_choose_saved_sign_in_for">Choose a saved sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
<!-- This appears as the title of the dialog asking for user to make a choice from various available user credentials (previously saved or to be created) to sign in to the app. [CHAR LIMIT=200] -->
- <string name="get_dialog_title_choose_sign_in_for">Choose a sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+ <string name="get_dialog_title_choose_sign_in_for">Choose an account for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
<!-- This appears as the title of the dialog asking for user to make a choice from options of available user information (e.g. driver's license, vaccination status) to pass to the app. [CHAR LIMIT=200] -->
<string name="get_dialog_title_choose_option_for">Choose an option for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
<!-- This appears as the title of the dialog asking user to send a piece of user information (e.g. driver's license, vaccination status) to the app. [CHAR LIMIT=200] -->
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
index ab70394..694e27a 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
@@ -21,7 +21,6 @@
import android.content.Intent
import android.credentials.selection.BaseDialogResult
import android.credentials.selection.BaseDialogResult.RESULT_CODE_DIALOG_USER_CANCELED
-import android.credentials.selection.Constants
import android.credentials.selection.ProviderPendingIntentResponse
import android.credentials.selection.UserSelectionDialogResult
import android.os.Bundle
@@ -117,21 +116,17 @@
sendCancellationCode(
cancelCode = cancelCode,
requestToken = token,
- resultReceiver = resultReceiver,
- finalResponseReceiver = finalResponseReceiver
+ resultReceiver = resultReceiver
)
}
private fun sendCancellationCode(
cancelCode: Int,
requestToken: IBinder?,
- resultReceiver: ResultReceiver?,
- finalResponseReceiver: ResultReceiver?
+ resultReceiver: ResultReceiver?
) {
if (requestToken != null && resultReceiver != null) {
- val resultData = Bundle().apply {
- putParcelable(Constants.EXTRA_FINAL_RESPONSE_RECEIVER, finalResponseReceiver)
- }
+ val resultData = Bundle()
BaseDialogResult.addToBundle(BaseDialogResult(requestToken), resultData)
resultReceiver.send(cancelCode, resultData)
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
index b408c15..9c8ec3b 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
@@ -142,7 +142,7 @@
isDefaultIconPreferredAsSingleProvider =
credentialEntry.isDefaultIconPreferredAsSingleProvider,
affiliatedDomain = credentialEntry.affiliatedDomain?.toString(),
- biometricRequest = predetermineAndValidateBiometricFlow(it,
+ biometricRequest = retrieveEntryBiometricRequest(it,
CREDENTIAL_ENTRY_PREFIX),
)
)
@@ -172,7 +172,7 @@
isDefaultIconPreferredAsSingleProvider =
credentialEntry.isDefaultIconPreferredAsSingleProvider,
affiliatedDomain = credentialEntry.affiliatedDomain?.toString(),
- biometricRequest = predetermineAndValidateBiometricFlow(it,
+ biometricRequest = retrieveEntryBiometricRequest(it,
CREDENTIAL_ENTRY_PREFIX),
)
)
@@ -201,7 +201,7 @@
isDefaultIconPreferredAsSingleProvider =
credentialEntry.isDefaultIconPreferredAsSingleProvider,
affiliatedDomain = credentialEntry.affiliatedDomain?.toString(),
- biometricRequest = predetermineAndValidateBiometricFlow(it,
+ biometricRequest = retrieveEntryBiometricRequest(it,
CREDENTIAL_ENTRY_PREFIX),
)
)
@@ -216,7 +216,7 @@
}
/**
- * This validates if this is a biometric flow or not, and if it is, this returns the expected
+ * This validates if the entry calling this method contains biometric info, and if so, returns a
* [BiometricRequestInfo]. Namely, the biometric flow must have at least the
* ALLOWED_AUTHENTICATORS bit passed from Jetpack.
* Note that the required values, such as the provider info's icon or display name, or the entries
@@ -230,7 +230,7 @@
* // TODO(b/326243754) : Presently, due to dependencies, the opId bit is parsed but is never
* // expected to be used. When it is added, it should be lightly validated.
*/
-fun predetermineAndValidateBiometricFlow(
+fun retrieveEntryBiometricRequest(
entry: Entry,
hintPrefix: String,
): BiometricRequestInfo? {
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
index 786c441..9242141 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
@@ -54,9 +54,3 @@
Constants.EXTRA_RESULT_RECEIVER,
ResultReceiver::class.java
)
-
-val Intent.finalResponseReceiver: ResultReceiver?
- get() = this.getParcelableExtra(
- Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
- ResultReceiver::class.java
- )
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
index 1683cc4..f1f1f7c 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
@@ -20,7 +20,6 @@
import android.content.Intent
import com.android.credentialmanager.ktx.getCredentialProviderDataList
import com.android.credentialmanager.ktx.requestInfo
-import com.android.credentialmanager.ktx.finalResponseReceiver
import com.android.credentialmanager.ktx.resultReceiver
import com.android.credentialmanager.ktx.toProviderList
import com.android.credentialmanager.model.Request
@@ -29,7 +28,6 @@
return Request.Get(
token = requestInfo?.token,
resultReceiver = resultReceiver,
- finalResponseReceiver = finalResponseReceiver,
providerInfos = getCredentialProviderDataList.toProviderList(context)
)
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
index fd99275..cb335fc 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -25,8 +25,7 @@
*/
sealed class Request private constructor(
open val token: IBinder?,
- open val resultReceiver: ResultReceiver? = null,
- open val finalResponseReceiver: ResultReceiver? = null,
+ open val resultReceiver: ResultReceiver? = null
) {
/**
@@ -51,9 +50,8 @@
data class Get(
override val token: IBinder?,
override val resultReceiver: ResultReceiver?,
- override val finalResponseReceiver: ResultReceiver?,
val providerInfos: List<ProviderInfo>,
- ) : Request(token, resultReceiver, finalResponseReceiver)
+ ) : Request(token, resultReceiver)
/**
* Request to start the create credentials flow.
*/
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index b17a98b..3683235 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -58,7 +58,6 @@
private val providerEnabledList: List<ProviderData>
private val providerDisabledList: List<DisabledProviderData>?
val resultReceiver: ResultReceiver?
- val finalResponseReceiver: ResultReceiver?
var initialUiState: UiState
@@ -105,12 +104,6 @@
Constants.EXTRA_RESULT_RECEIVER,
ResultReceiver::class.java
)
-
- finalResponseReceiver = intent.getParcelableExtra(
- Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
- ResultReceiver::class.java
- )
-
isReqForAllOptions = requestInfo?.isShowAllOptionsRequested ?: false
val cancellationRequest = getCancelUiRequest(intent)
@@ -206,7 +199,7 @@
}
fun onCancel(cancelCode: Int) {
- sendCancellationCode(cancelCode, requestInfo?.token, resultReceiver, finalResponseReceiver)
+ sendCancellationCode(cancelCode, requestInfo?.token, resultReceiver)
}
fun onOptionSelected(
@@ -226,9 +219,6 @@
val resultDataBundle = Bundle()
UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultDataBundle)
- resultDataBundle.putParcelable(Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
- finalResponseReceiver)
-
resultReceiver?.send(
BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
resultDataBundle
@@ -296,13 +286,10 @@
fun sendCancellationCode(
cancelCode: Int,
requestToken: IBinder?,
- resultReceiver: ResultReceiver?,
- finalResponseReceiver: ResultReceiver?
+ resultReceiver: ResultReceiver?
) {
if (requestToken != null && resultReceiver != null) {
val resultData = Bundle()
- resultData.putParcelable(Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
- finalResponseReceiver)
BaseDialogResult.addToBundle(BaseDialogResult(requestToken), resultData)
resultReceiver.send(cancelCode, resultData)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index ec0da09..a2f55cd 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -208,18 +208,13 @@
android.credentials.selection.Constants.EXTRA_RESULT_RECEIVER,
ResultReceiver::class.java
)
- val finalResponseResultReceiver = intent.getParcelableExtra(
- android.credentials.selection.Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
- ResultReceiver::class.java
- )
-
val requestInfo = intent.extras?.getParcelable(
RequestInfo.EXTRA_REQUEST_INFO,
RequestInfo::class.java
)
CredentialManagerRepo.sendCancellationCode(
BaseDialogResult.RESULT_CODE_DATA_PARSING_FAILURE,
- requestInfo?.token, resultReceiver, finalResponseResultReceiver
+ requestInfo?.token, resultReceiver
)
this.finish()
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index a039753..429bdbf 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -30,6 +30,9 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
+import com.android.credentialmanager.common.BiometricError
+import com.android.credentialmanager.common.BiometricFlowType
+import com.android.credentialmanager.common.BiometricPromptState
import com.android.credentialmanager.common.BiometricResult
import com.android.credentialmanager.common.BiometricState
import com.android.credentialmanager.model.EntryInfo
@@ -40,7 +43,7 @@
import com.android.credentialmanager.createflow.ActiveEntry
import com.android.credentialmanager.createflow.CreateCredentialUiState
import com.android.credentialmanager.createflow.CreateScreenState
-import com.android.credentialmanager.createflow.findBiometricFlowEntry
+import com.android.credentialmanager.createflow.isBiometricFlow
import com.android.credentialmanager.getflow.GetCredentialUiState
import com.android.credentialmanager.getflow.GetScreenState
import com.android.credentialmanager.logging.LifecycleEvent
@@ -126,13 +129,22 @@
uiState = uiState.copy(providerActivityState = ProviderActivityState.PENDING)
val entryIntent = entry.fillInIntent
entryIntent?.putExtra(Constants.IS_AUTO_SELECTED_KEY, uiState.isAutoSelectFlow)
- if (biometricState.biometricResult != null) {
+ if (biometricState.biometricResult != null || biometricState.biometricError != null) {
if (uiState.isAutoSelectFlow) {
Log.w(Constants.LOG_TAG, "Unexpected biometric result exists when " +
"autoSelect is preferred.")
}
- entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_TYPE,
- biometricState.biometricResult.biometricAuthenticationResult.authenticationType)
+ // TODO(b/333445754) : Decide whether to propagate info on prompt launch
+ if (biometricState.biometricResult != null) {
+ entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_RESULT,
+ biometricState.biometricResult.biometricAuthenticationResult
+ .authenticationType)
+ } else if (biometricState.biometricError != null){
+ entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_ERROR_CODE,
+ biometricState.biometricError.errorCode)
+ entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_ERROR_MESSAGE,
+ biometricState.biometricError.errorMessage)
+ }
}
val intentSenderRequest = IntentSenderRequest.Builder(pendingIntent)
.setFillInIntent(entryIntent).build()
@@ -217,7 +229,8 @@
/**************************************************************************/
fun getFlowOnEntrySelected(
entry: EntryInfo,
- authResult: BiometricPrompt.AuthenticationResult? = null
+ authResult: BiometricPrompt.AuthenticationResult? = null,
+ authError: BiometricError? = null,
) {
Log.d(Constants.LOG_TAG, "credential selected: {provider=${entry.providerId}" +
", key=${entry.entryKey}, subkey=${entry.entrySubkey}}")
@@ -225,10 +238,11 @@
uiState.copy(
selectedEntry = entry,
providerActivityState = ProviderActivityState.READY_TO_LAUNCH,
- biometricState = if (authResult == null) uiState.biometricState else uiState
+ biometricState = if (authResult == null && authError == null)
+ uiState.biometricState else if (authResult != null) uiState
.biometricState.copy(biometricResult = BiometricResult(
- biometricAuthenticationResult = authResult)
- )
+ biometricAuthenticationResult = authResult)) else uiState
+ .biometricState.copy(biometricError = authError)
)
} else {
credManRepo.onOptionSelected(entry.providerId, entry.entryKey, entry.entrySubkey)
@@ -256,6 +270,15 @@
)
}
+ fun getFlowOnMoreOptionOnlySelected() {
+ Log.d(Constants.LOG_TAG, "More Option Only selected")
+ uiState = uiState.copy(
+ getCredentialUiState = uiState.getCredentialUiState?.copy(
+ currentScreenState = GetScreenState.ALL_SIGN_IN_OPTIONS_ONLY
+ )
+ )
+ }
+
fun getFlowOnMoreOptionOnSnackBarSelected(isNoAccount: Boolean) {
Log.d(Constants.LOG_TAG, "More Option on snackBar selected")
uiState = uiState.copy(
@@ -303,13 +326,23 @@
}
fun createFlowOnEntrySelectedFromMoreOptionScreen(activeEntry: ActiveEntry) {
+ val isBiometricFlow = isBiometricFlow(activeEntry = activeEntry, isAutoSelectFlow = false)
+ if (isBiometricFlow) {
+ // This atomically ensures that the only edge case that *restarts* the biometric flow
+ // doesn't risk a configuration change bug on the more options page during create.
+ // Namely, it's atomic in that it happens only on a tap, and it is not possible to
+ // reproduce a tap and a rotation at the same time. However, even if it were, it would
+ // just be an alternate way to jump back into the biometric selection flow after this
+ // reset, and thus, the state machine is maintained.
+ onBiometricPromptStateChange(BiometricPromptState.INACTIVE)
+ }
uiState = uiState.copy(
createCredentialUiState = uiState.createCredentialUiState?.copy(
currentScreenState =
// An autoselect flow never makes it to the more options screen
- if (findBiometricFlowEntry(activeEntry = activeEntry,
- isAutoSelectFlow = false) != null) CreateScreenState.BIOMETRIC_SELECTION
- else if (
+ if (isBiometricFlow) {
+ CreateScreenState.BIOMETRIC_SELECTION
+ } else if (
uiState.createCredentialUiState?.requestDisplayInfo?.userSetDefaultProviderIds
?.contains(activeEntry.activeProvider.id) ?: true ||
!(uiState.createCredentialUiState?.foundCandidateFromUserDefaultProvider
@@ -338,7 +371,8 @@
fun createFlowOnEntrySelected(
selectedEntry: EntryInfo,
- authResult: AuthenticationResult? = null
+ authResult: AuthenticationResult? = null,
+ authError: BiometricError? = null,
) {
val providerId = selectedEntry.providerId
val entryKey = selectedEntry.entryKey
@@ -350,9 +384,11 @@
uiState = uiState.copy(
selectedEntry = selectedEntry,
providerActivityState = ProviderActivityState.READY_TO_LAUNCH,
- biometricState = if (authResult == null) uiState.biometricState else uiState
+ biometricState = if (authResult == null && authError == null)
+ uiState.biometricState else if (authResult != null) uiState
.biometricState.copy(biometricResult = BiometricResult(
- biometricAuthenticationResult = authResult))
+ biometricAuthenticationResult = authResult)) else uiState
+ .biometricState.copy(biometricError = authError)
)
} else {
credManRepo.onOptionSelected(
@@ -375,6 +411,46 @@
}
}
+ /**************************************************************************/
+ /***** Biometric Flow Callbacks *****/
+ /**************************************************************************/
+
+ /**
+ * This allows falling back from the biometric prompt screen to the normal get flow by applying
+ * a reset to all necessary states involved in the fallback.
+ */
+ fun fallbackFromBiometricToNormalFlow(biometricFlowType: BiometricFlowType) {
+ onBiometricPromptStateChange(BiometricPromptState.INACTIVE)
+ when (biometricFlowType) {
+ BiometricFlowType.GET -> getFlowOnBackToPrimarySelectionScreen()
+ BiometricFlowType.CREATE -> createFlowOnUseOnceSelected()
+ }
+ }
+
+ /**
+ * This method can be used to change the [BiometricPromptState] according to the necessity.
+ * For example, if resetting, one might use [BiometricPromptState.INACTIVE], but if the flow
+ * has just launched, to avoid configuration errors, one can use
+ * [BiometricPromptState.PENDING].
+ */
+ fun onBiometricPromptStateChange(biometricPromptState: BiometricPromptState) {
+ uiState = uiState.copy(
+ biometricState = uiState.biometricState.copy(
+ biometricStatus = biometricPromptState
+ )
+ )
+ }
+
+ /**
+ * This returns the present biometric state.
+ */
+ fun getBiometricPromptState(): BiometricPromptState =
+ uiState.biometricState.biometricStatus
+
+ /**************************************************************************/
+ /***** Misc. Callbacks/Logs *****/
+ /**************************************************************************/
+
@Composable
fun logUiEvent(uiEventEnum: UiEventEnum) {
this.uiMetrics.log(uiEventEnum, credManRepo.requestInfo?.packageName)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 358ebfa..c477f30 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -53,8 +53,9 @@
import org.json.JSONObject
import android.credentials.flags.Flags
import com.android.credentialmanager.createflow.isBiometricFlow
+import com.android.credentialmanager.createflow.isFlowAutoSelectable
import com.android.credentialmanager.getflow.TopBrandingContent
-import com.android.credentialmanager.ktx.predetermineAndValidateBiometricFlow
+import com.android.credentialmanager.ktx.retrieveEntryBiometricRequest
import java.time.Instant
fun getAppLabel(
@@ -431,7 +432,12 @@
remoteEntryProvider = remoteEntryProvider,
)
val isBiometricFlow = if (activeEntry == null) false else isBiometricFlow(activeEntry,
- sortedCreateOptionsPairs, requestDisplayInfo)
+ isFlowAutoSelectable(
+ requestDisplayInfo = requestDisplayInfo,
+ activeEntry = activeEntry,
+ sortedCreateOptionsPairs = sortedCreateOptionsPairs
+ )
+ )
val initialScreenState = toCreateScreenState(
createOptionSize = createOptionsPairs.size,
remoteEntry = remoteEntry,
@@ -514,7 +520,7 @@
it.hasHint("androidx.credentials.provider.createEntry.SLICE_HINT_AUTO_" +
"SELECT_ALLOWED")
}?.text == "true",
- biometricRequest = predetermineAndValidateBiometricFlow(it,
+ biometricRequest = retrieveEntryBiometricRequest(it,
CREATE_ENTRY_PREFIX),
)
)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 1253ce3..50ebdd5 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -32,6 +32,7 @@
import android.os.Bundle
import android.os.CancellationSignal
import android.os.OutcomeReceiver
+import android.os.ResultReceiver
import android.service.autofill.AutofillService
import android.service.autofill.Dataset
import android.service.autofill.Field
@@ -46,9 +47,9 @@
import android.service.credentials.CredentialProviderService
import android.util.Log
import android.content.Intent
+import android.os.IBinder
import android.view.autofill.AutofillId
import android.view.autofill.AutofillManager
-import android.view.autofill.IAutoFillManagerClient
import android.widget.RemoteViews
import android.widget.inline.InlinePresentationSpec
import androidx.autofill.inline.v1.InlineSuggestionUi
@@ -94,7 +95,7 @@
request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback,
- autofillCallback: IAutoFillManagerClient
+ autofillCallback: IBinder
) {
val context = request.fillContexts
val structure = context[context.size - 1].structure
@@ -109,17 +110,19 @@
}
val sessionId = clientState.getInt(SESSION_ID_KEY)
val requestId = clientState.getInt(REQUEST_ID_KEY)
+ val resultReceiver = clientState.getParcelable(
+ CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, ResultReceiver::class.java)
Log.i(TAG, "Autofill sessionId: $sessionId, autofill requestId: $requestId")
- if (sessionId == 0 || requestId == 0) {
- Log.i(TAG, "Session Id or request Id not found")
- callback.onFailure("Session Id or request Id not found")
+ if (sessionId == 0 || requestId == 0 || resultReceiver == null) {
+ Log.i(TAG, "Session Id or request Id or resultReceiver not found")
+ callback.onFailure("Session Id or request Id or resultReceiver not found")
return
}
val responseClientState = Bundle()
responseClientState.putBoolean(WEBVIEW_REQUESTED_CREDENTIAL_KEY, false)
val getCredRequest: GetCredentialRequest? = getCredManRequest(structure, sessionId,
- requestId, responseClientState)
+ requestId, resultReceiver, responseClientState)
// TODO(b/324635774): Use callback for validating. If the request is coming
// directly from the view, there should be a corresponding callback, otherwise
// we should fail fast,
@@ -157,7 +160,7 @@
CancellationSignal(),
Executors.newSingleThreadExecutor(),
outcome,
- autofillCallback.asBinder()
+ autofillCallback
)
}
@@ -531,6 +534,7 @@
structure: AssistStructure,
sessionId: Int,
requestId: Int,
+ resultReceiver: ResultReceiver,
responseClientState: Bundle
): GetCredentialRequest? {
val credentialOptions: MutableList<CredentialOption> = mutableListOf()
@@ -540,6 +544,9 @@
val dataBundle = Bundle()
dataBundle.putInt(SESSION_ID_KEY, sessionId)
dataBundle.putInt(REQUEST_ID_KEY, requestId)
+ dataBundle.putParcelable(CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER,
+ resultReceiver)
+
return GetCredentialRequest.Builder(dataBundle)
.setCredentialOptions(credentialOptions)
.build()
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricFlowType.kt
similarity index 95%
rename from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
rename to packages/CredentialManager/src/com/android/credentialmanager/common/BiometricFlowType.kt
index f6140f5..263cfd5 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricFlowType.kt
@@ -16,7 +16,7 @@
package com.android.credentialmanager.common
-enum class FlowType {
+enum class BiometricFlowType {
GET,
CREATE
}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
index fa17735..6bf803a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
@@ -17,9 +17,12 @@
package com.android.credentialmanager.common
import android.content.Context
+import android.content.DialogInterface
import android.graphics.Bitmap
import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.BiometricManager.Authenticators
import android.hardware.biometrics.BiometricPrompt
+import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
import android.os.CancellationSignal
import android.util.Log
import androidx.core.content.ContextCompat.getMainExecutor
@@ -43,30 +46,35 @@
* Namely, this adds the ability to encapsulate the [providerIcon], the providers icon, the
* [providerName], which represents the name of the provider, the [displayTitleText] which is
* the large text displaying the flow in progress, and the [descriptionForCredential], which
- * describes details of where the credential is being saved, and how.
- * (E.g. assume a hypothetical provider 'Any Provider' for *passkey* flows with Your@Email.com:
+ * describes details of where the credential is being saved, and how. [displaySubtitleText] is only expected
+ * to be used by the 'create' flow, optionally, and describes the saved name of the creating entity.
+ * (E.g. assume a hypothetical provider 'Any Provider' for *passkey* flows with Your@Email.com and
+ * name 'Your', and an rp called 'The App'):
*
* 'get' flow:
* - [providerIcon] and [providerName] = 'Any Provider' (and it's icon)
- * - [displayTitleText] = "Use your saved passkey for Any Provider?"
- * - [descriptionForCredential] = "Use your screen lock to sign in to Any Provider with
- * Your@Email.com"
+ * - [displayTitleText] = "Use your saved passkey for The App?"
+ * - [descriptionForCredential] = "Sign in to The App with your saved passkey for
+ * Your@gmail.com"
*
* 'create' flow:
* - [providerIcon] and [providerName] = 'Any Provider' (and it's icon)
* - [displayTitleText] = "Create passkey to sign in to Any Provider?"
- * - [descriptionForCredential] = "Use your screen lock to create a passkey for Any Provider?"
+ * - [subtitle] = "Your"
+ * - [descriptionForCredential] = "You can use your passkey on other devices. It is saved to
+ * * Google Password Manager for Your@gmail.com."
* ).
*
* The above are examples; the credential type can change depending on scenario.
- * // TODO(b/326243891) : Finalize once all the strings and create flow is iterated to completion
+ * // TODO(b/333445112) : Finalize once all the strings and create flow is iterated to completion
*/
data class BiometricDisplayInfo(
val providerIcon: Bitmap,
val providerName: String,
val displayTitleText: String,
- val descriptionForCredential: String,
+ val descriptionForCredential: String?,
val biometricRequestInfo: BiometricRequestInfo,
+ val displaySubtitleText: CharSequence? = null,
)
/**
@@ -75,7 +83,9 @@
* additional states that may improve the flow.
*/
data class BiometricState(
- val biometricResult: BiometricResult? = null
+ val biometricResult: BiometricResult? = null,
+ val biometricError: BiometricError? = null,
+ val biometricStatus: BiometricPromptState = BiometricPromptState.INACTIVE
)
/**
@@ -83,7 +93,7 @@
* so that should this object exist, the result will be retrievable.
*/
data class BiometricResult(
- val biometricAuthenticationResult: BiometricPrompt.AuthenticationResult
+ val biometricAuthenticationResult: BiometricPrompt.AuthenticationResult,
)
/**
@@ -91,156 +101,262 @@
*/
data class BiometricError(
val errorCode: Int,
- val errString: CharSequence? = null
+ val errorMessage: CharSequence? = null
)
/**
- * Encapsulates the help callback results to easily manage biometric help states in the flow.
- * To specify, this allows us to parse the onAuthenticationHelp method in the [BiometricPrompt].
+ * This is the entry point to start the integrated biometric prompt for 'get' flows. It captures
+ * information specific to the get flow, along with required shared callbacks and more general
+ * info across both flows, such as the tapped [EntryInfo] or [sendDataToProvider].
*/
-data class BiometricHelp(
- val helpCode: Int,
- var helpString: CharSequence? = null
-)
+fun runBiometricFlowForGet(
+ biometricEntry: EntryInfo,
+ context: Context,
+ openMoreOptionsPage: () -> Unit,
+ sendDataToProvider: (EntryInfo, BiometricPrompt.AuthenticationResult?, BiometricError?) -> Unit,
+ onCancelFlowAndFinish: () -> Unit,
+ onIllegalStateAndFinish: (String) -> Unit,
+ getBiometricPromptState: () -> BiometricPromptState,
+ onBiometricPromptStateChange: (BiometricPromptState) -> Unit,
+ onBiometricFailureFallback: (BiometricFlowType) -> Unit,
+ getRequestDisplayInfo: RequestDisplayInfo? = null,
+ getProviderInfoList: List<ProviderInfo>? = null,
+ getProviderDisplayInfo: ProviderDisplayInfo? = null,
+) {
+ if (getBiometricPromptState() != BiometricPromptState.INACTIVE) {
+ // Screen is already up, do not re-launch
+ return
+ }
+ onBiometricPromptStateChange(BiometricPromptState.PENDING)
+ val biometricDisplayInfo = validateAndRetrieveBiometricGetDisplayInfo(
+ getRequestDisplayInfo,
+ getProviderInfoList,
+ getProviderDisplayInfo,
+ context, biometricEntry
+ )
+
+ if (biometricDisplayInfo == null) {
+ onBiometricFailureFallback(BiometricFlowType.GET)
+ return
+ }
+
+ val callback: BiometricPrompt.AuthenticationCallback =
+ setupBiometricAuthenticationCallback(sendDataToProvider, biometricEntry,
+ onCancelFlowAndFinish, onIllegalStateAndFinish, onBiometricPromptStateChange)
+
+ Log.d(TAG, "The BiometricPrompt API call begins.")
+ runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage,
+ onBiometricFailureFallback, BiometricFlowType.GET, onCancelFlowAndFinish)
+}
+
+/**
+ * This is the entry point to start the integrated biometric prompt for 'create' flows. It captures
+ * information specific to the create flow, along with required shared callbacks and more general
+ * info across both flows, such as the tapped [EntryInfo] or [sendDataToProvider].
+ */
+fun runBiometricFlowForCreate(
+ biometricEntry: EntryInfo,
+ context: Context,
+ openMoreOptionsPage: () -> Unit,
+ sendDataToProvider: (EntryInfo, BiometricPrompt.AuthenticationResult?, BiometricError?) -> Unit,
+ onCancelFlowAndFinish: () -> Unit,
+ onIllegalStateAndFinish: (String) -> Unit,
+ getBiometricPromptState: () -> BiometricPromptState,
+ onBiometricPromptStateChange: (BiometricPromptState) -> Unit,
+ onBiometricFailureFallback: (BiometricFlowType) -> Unit,
+ createRequestDisplayInfo: com.android.credentialmanager.createflow
+ .RequestDisplayInfo? = null,
+ createProviderInfo: EnabledProviderInfo? = null,
+) {
+ if (getBiometricPromptState() != BiometricPromptState.INACTIVE) {
+ // Screen is already up, do not re-launch
+ return
+ }
+ onBiometricPromptStateChange(BiometricPromptState.PENDING)
+ val biometricDisplayInfo = validateAndRetrieveBiometricCreateDisplayInfo(
+ createRequestDisplayInfo,
+ createProviderInfo,
+ context, biometricEntry
+ )
+
+ if (biometricDisplayInfo == null) {
+ onBiometricFailureFallback(BiometricFlowType.CREATE)
+ return
+ }
+
+ val callback: BiometricPrompt.AuthenticationCallback =
+ setupBiometricAuthenticationCallback(sendDataToProvider, biometricEntry,
+ onCancelFlowAndFinish, onIllegalStateAndFinish, onBiometricPromptStateChange)
+
+ Log.d(TAG, "The BiometricPrompt API call begins.")
+ runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage,
+ onBiometricFailureFallback, BiometricFlowType.CREATE, onCancelFlowAndFinish)
+}
/**
* This will handle the logic for integrating credential manager with the biometric prompt for the
* single account biometric experience. This simultaneously handles both the get and create flows,
* by retrieving all the data from credential manager, and properly parsing that data into the
- * biometric prompt.
+ * biometric prompt. It will fallback in cases where the biometric api cannot be called, or when
+ * only device credentials are requested.
*/
-fun runBiometricFlow(
- biometricEntry: EntryInfo,
+private fun runBiometricFlow(
context: Context,
+ biometricDisplayInfo: BiometricDisplayInfo,
+ callback: BiometricPrompt.AuthenticationCallback,
openMoreOptionsPage: () -> Unit,
- sendDataToProvider: (EntryInfo, BiometricPrompt.AuthenticationResult) -> Unit,
- onCancelFlowAndFinish: () -> Unit,
- onIllegalStateAndFinish: (String) -> Unit,
- getRequestDisplayInfo: RequestDisplayInfo? = null,
- getProviderInfoList: List<ProviderInfo>? = null,
- getProviderDisplayInfo: ProviderDisplayInfo? = null,
- onBiometricFailureFallback: () -> Unit,
- createRequestDisplayInfo: com.android.credentialmanager.createflow
- .RequestDisplayInfo? = null,
- createProviderInfo: EnabledProviderInfo? = null,
+ onBiometricFailureFallback: (BiometricFlowType) -> Unit,
+ biometricFlowType: BiometricFlowType,
+ onCancelFlowAndFinish: () -> Unit
) {
- // TODO(b/330396089) : Add rotation configuration fix with state machine
- var biometricDisplayInfo: BiometricDisplayInfo? = null
- var flowType = FlowType.GET
- if (getRequestDisplayInfo != null) {
- biometricDisplayInfo = validateAndRetrieveBiometricGetDisplayInfo(getRequestDisplayInfo,
- getProviderInfoList,
- getProviderDisplayInfo,
- context, biometricEntry)
- } else if (createRequestDisplayInfo != null) {
- flowType = FlowType.CREATE
- biometricDisplayInfo = validateAndRetrieveBiometricCreateDisplayInfo(
- createRequestDisplayInfo,
- createProviderInfo,
- context, biometricEntry)
- }
-
- if (biometricDisplayInfo == null) {
- onBiometricFailureFallback()
- return
- }
-
- val biometricPrompt = setupBiometricPrompt(context, biometricDisplayInfo, openMoreOptionsPage,
- biometricDisplayInfo.biometricRequestInfo.allowedAuthenticators, flowType)
-
- val callback: BiometricPrompt.AuthenticationCallback =
- setupBiometricAuthenticationCallback(sendDataToProvider, biometricEntry,
- onCancelFlowAndFinish, onIllegalStateAndFinish)
-
- val cancellationSignal = CancellationSignal()
- cancellationSignal.setOnCancelListener {
- Log.d(TAG, "Your cancellation signal was called.")
- // TODO(b/326243754) : Migrate towards passing along the developer cancellation signal
- // or validate the necessity for this
- }
-
- val executor = getMainExecutor(context)
-
try {
- biometricPrompt.authenticate(cancellationSignal, executor, callback)
+ if (onlyUsingDeviceCredentials(biometricDisplayInfo, context)) {
+ onBiometricFailureFallback(biometricFlowType)
+ return
+ }
+
+ val biometricPrompt = setupBiometricPrompt(context, biometricDisplayInfo,
+ openMoreOptionsPage, biometricDisplayInfo.biometricRequestInfo, onCancelFlowAndFinish)
+
+ val cancellationSignal = CancellationSignal()
+ cancellationSignal.setOnCancelListener {
+ Log.d(TAG, "Your cancellation signal was called.")
+ // TODO(b/333445112) : Migrate towards passing along the developer cancellation signal
+ // or validate the necessity for this
+ }
+
+ val executor = getMainExecutor(context)
+
+ val cryptoOpId = getCryptoOpId(biometricDisplayInfo)
+ if (cryptoOpId != null) {
+ biometricPrompt.authenticate(
+ BiometricPrompt.CryptoObject(cryptoOpId.toLong()),
+ cancellationSignal, executor, callback)
+ } else {
+ biometricPrompt.authenticate(cancellationSignal, executor, callback)
+ }
} catch (e: IllegalArgumentException) {
Log.w(TAG, "Calling the biometric prompt API failed with: /n${e.localizedMessage}\n")
- onBiometricFailureFallback()
+ onBiometricFailureFallback(biometricFlowType)
}
}
+private fun getCryptoOpId(biometricDisplayInfo: BiometricDisplayInfo): Int? {
+ return biometricDisplayInfo.biometricRequestInfo.opId
+}
+
+/**
+ * Determines if, given the allowed authenticators, the flow should fallback early. This has
+ * consistency because for biometrics to exist, **device credentials must exist**. Thus, fallbacks
+ * occur if *only* device credentials are available, to avoid going right into the PIN screen.
+ * Note that if device credential is the only available modality but not requested, or if none
+ * of the requested modalities are available, we propagate the error to the provider instead of
+ * falling back and expect them to handle it as they would prior.
+ * // TODO(b/334197980) : Finalize error propagation/not propagation in real use cases
+ */
+private fun onlyUsingDeviceCredentials(
+ biometricDisplayInfo: BiometricDisplayInfo,
+ context: Context
+): Boolean {
+ val allowedAuthenticators = biometricDisplayInfo.biometricRequestInfo.allowedAuthenticators
+ if (allowedAuthenticators == BiometricManager.Authenticators.DEVICE_CREDENTIAL) {
+ return true
+ }
+
+ val allowedAuthContainsDeviceCredential = containsBiometricAuthenticatorWithDeviceCredentials(
+ allowedAuthenticators)
+
+ if (!allowedAuthContainsDeviceCredential) {
+ // At this point, allowed authenticators is requesting biometrics without device creds.
+ // Thus, a fallback mechanism will be displayed via our own negative button - "cancel".
+ // Beyond this point, fallbacks will occur if none of the stronger authenticators can
+ // be used.
+ return false
+ }
+
+ val biometricManager = context.getSystemService(Context.BIOMETRIC_SERVICE) as BiometricManager
+
+ if (allowedAuthContainsDeviceCredential &&
+ biometricManager.canAuthenticate(Authenticators.BIOMETRIC_WEAK) !=
+ BiometricManager.BIOMETRIC_SUCCESS &&
+ biometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG) !=
+ BiometricManager.BIOMETRIC_SUCCESS) {
+ return true
+ }
+
+ return false
+}
+
+private fun containsBiometricAuthenticatorWithDeviceCredentials(
+ allowedAuthenticators: Int
+): Boolean {
+ val allowedAuthContainsDeviceCredential = (allowedAuthenticators ==
+ Authenticators.BIOMETRIC_WEAK or Authenticators.DEVICE_CREDENTIAL) ||
+ (allowedAuthenticators ==
+ Authenticators.BIOMETRIC_STRONG or Authenticators.DEVICE_CREDENTIAL)
+ return allowedAuthContainsDeviceCredential
+}
+
/**
* Sets up the biometric prompt with the UI specific bits.
- * // TODO(b/326243754) : Pass in opId once dependency is confirmed via CryptoObject
+ * // TODO(b/333445112) : Pass in opId once dependency is confirmed via CryptoObject
*/
private fun setupBiometricPrompt(
context: Context,
biometricDisplayInfo: BiometricDisplayInfo,
openMoreOptionsPage: () -> Unit,
- requestAllowedAuthenticators: Int,
- flowType: FlowType,
+ biometricRequestInfo: BiometricRequestInfo,
+ onCancelFlowAndFinish: () -> Unit
): BiometricPrompt {
- val finalAuthenticators = removeDeviceCredential(requestAllowedAuthenticators)
+ val listener =
+ DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> openMoreOptionsPage() }
- val biometricPrompt = BiometricPrompt.Builder(context)
+ val promptContentViewBuilder = PromptContentViewWithMoreOptionsButton.Builder()
+ .setMoreOptionsButtonListener(context.mainExecutor, listener)
+ biometricDisplayInfo.descriptionForCredential?.let {
+ promptContentViewBuilder.setDescription(it) }
+
+ val biometricPromptBuilder = BiometricPrompt.Builder(context)
.setTitle(biometricDisplayInfo.displayTitleText)
- // TODO(b/326243754) : Migrate to using new methods recently aligned upon
- .setNegativeButton(context.getString(if (flowType == FlowType.GET) R.string
- .dropdown_presentation_more_sign_in_options_text else R.string.string_more_options),
- getMainExecutor(context)) { _, _ ->
- openMoreOptionsPage()
- }
- .setAllowedAuthenticators(finalAuthenticators)
+ .setAllowedAuthenticators(biometricRequestInfo.allowedAuthenticators)
.setConfirmationRequired(true)
.setLogoBitmap(biometricDisplayInfo.providerIcon)
.setLogoDescription(biometricDisplayInfo.providerName)
- .setDescription(biometricDisplayInfo.descriptionForCredential)
- .build()
+ .setContentView(promptContentViewBuilder.build())
- return biometricPrompt
-}
-
-// TODO(b/326243754) : Remove after larger level alignments made on fallback negative button
-// For the time being, we do not support the pin fallback until UX is decided.
-private fun removeDeviceCredential(requestAllowedAuthenticators: Int): Int {
- var finalAuthenticators = requestAllowedAuthenticators
-
- if (requestAllowedAuthenticators == (BiometricManager.Authenticators.DEVICE_CREDENTIAL or
- BiometricManager.Authenticators.BIOMETRIC_WEAK)) {
- finalAuthenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
+ if (!containsBiometricAuthenticatorWithDeviceCredentials(biometricDisplayInfo
+ .biometricRequestInfo.allowedAuthenticators)) {
+ biometricPromptBuilder.setNegativeButton(context.getString(R.string.string_cancel),
+ getMainExecutor(context)
+ ) { _: DialogInterface?, _: Int -> onCancelFlowAndFinish() }
}
- if (requestAllowedAuthenticators == (BiometricManager.Authenticators.DEVICE_CREDENTIAL or
- BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
- finalAuthenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
- }
+ biometricDisplayInfo.displaySubtitleText?.let { biometricPromptBuilder.setSubtitle(it) }
- if (requestAllowedAuthenticators == (BiometricManager.Authenticators.DEVICE_CREDENTIAL)) {
- finalAuthenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
- }
-
- return finalAuthenticators
+ return biometricPromptBuilder.build()
}
/**
* Sets up the biometric authentication callback.
*/
private fun setupBiometricAuthenticationCallback(
- sendDataToProvider: (EntryInfo, BiometricPrompt.AuthenticationResult) -> Unit,
+ sendDataToProvider: (EntryInfo, BiometricPrompt.AuthenticationResult?, BiometricError?) -> Unit,
selectedEntry: EntryInfo,
onCancelFlowAndFinish: () -> Unit,
onIllegalStateAndFinish: (String) -> Unit,
+ onBiometricPromptStateChange: (BiometricPromptState) -> Unit
): BiometricPrompt.AuthenticationCallback {
val callback: BiometricPrompt.AuthenticationCallback =
object : BiometricPrompt.AuthenticationCallback() {
- // TODO(b/326243754) : Validate remaining callbacks
+ // TODO(b/333445772) : Validate remaining callbacks
override fun onAuthenticationSucceeded(
authResult: BiometricPrompt.AuthenticationResult?
) {
super.onAuthenticationSucceeded(authResult)
try {
if (authResult != null) {
- sendDataToProvider(selectedEntry, authResult)
+ onBiometricPromptStateChange(BiometricPromptState.COMPLETE)
+ sendDataToProvider(selectedEntry, authResult, /*authError=*/null)
} else {
onIllegalStateAndFinish("The biometric flow succeeded but unexpectedly " +
"returned a null value.")
@@ -254,26 +370,26 @@
override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?) {
super.onAuthenticationHelp(helpCode, helpString)
Log.d(TAG, "Authentication help discovered: $helpCode and $helpString")
- // TODO(b/326243754) : Decide on strategy with provider (a simple log probably
- // suffices here)
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
super.onAuthenticationError(errorCode, errString)
Log.d(TAG, "Authentication error-ed out: $errorCode and $errString")
+ onBiometricPromptStateChange(BiometricPromptState.COMPLETE)
if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED) {
// Note that because the biometric prompt is imbued directly
// into the selector, parity applies to the selector's cancellation instead
// of the provider's biometric prompt cancellation.
onCancelFlowAndFinish()
+ } else {
+ sendDataToProvider(selectedEntry, /*authResult=*/null, /*authError=*/
+ BiometricError(errorCode, errString))
}
- // TODO(b/326243754) : Propagate to provider
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
Log.d(TAG, "Authentication failed.")
- // TODO(b/326243754) : Propagate to provider
}
}
return callback
@@ -299,7 +415,7 @@
if (getRequestDisplayInfo != null && getProviderInfoList != null &&
getProviderDisplayInfo != null) {
if (selectedEntry !is CredentialEntryInfo) { return null }
- return getBiometricDisplayValues(getProviderInfoList,
+ return retrieveBiometricGetDisplayValues(getProviderInfoList,
context, getRequestDisplayInfo, selectedEntry)
}
return null
@@ -308,7 +424,8 @@
/**
* Creates the [BiometricDisplayInfo] for create flows, and early handles conditional
* checking between the two. The reason for this method matches the logic for the
- * [validateBiometricGetFlow] with the only difference being that this is for the create flow.
+ * [validateAndRetrieveBiometricGetDisplayInfo] with the only difference being that this is for
+ * the create flow.
*/
private fun validateAndRetrieveBiometricCreateDisplayInfo(
createRequestDisplayInfo: com.android.credentialmanager.createflow.RequestDisplayInfo?,
@@ -318,8 +435,8 @@
): BiometricDisplayInfo? {
if (createRequestDisplayInfo != null && createProviderInfo != null) {
if (selectedEntry !is CreateOptionInfo) { return null }
- return createBiometricDisplayValues(createRequestDisplayInfo, createProviderInfo, context,
- selectedEntry)
+ return retrieveBiometricCreateDisplayValues(createRequestDisplayInfo, createProviderInfo,
+ context, selectedEntry)
}
return null
}
@@ -330,16 +447,16 @@
* to the original selector. Note that these redundant checks are just failsafe; the original
* flow should never reach here with invalid params.
*/
-private fun getBiometricDisplayValues(
+private fun retrieveBiometricGetDisplayValues(
getProviderInfoList: List<ProviderInfo>,
context: Context,
getRequestDisplayInfo: RequestDisplayInfo,
selectedEntry: CredentialEntryInfo,
): BiometricDisplayInfo? {
- var icon: Bitmap? = null
- var providerName: String? = null
- var displayTitleText: String? = null
- var descriptionText: String? = null
+ val icon: Bitmap?
+ val providerName: String?
+ val displayTitleText: String?
+ val descriptionText: String?
val primaryAccountsProviderInfo = retrievePrimaryAccountProviderInfo(selectedEntry.providerId,
getProviderInfoList)
icon = primaryAccountsProviderInfo?.icon?.toBitmap()
@@ -354,15 +471,29 @@
}
val singleEntryType = selectedEntry.credentialType
val username = selectedEntry.userName
+
+ // TODO(b/330396140) : Finalize localization and parsing for specific sign in option flows
+ // (fingerprint, face, etc...))
displayTitleText = context.getString(
generateDisplayTitleTextResCode(singleEntryType),
getRequestDisplayInfo.appName
)
+
descriptionText = context.getString(
- R.string.get_dialog_title_single_tap_for,
+ when (singleEntryType) {
+ CredentialType.PASSKEY ->
+ R.string.get_dialog_description_single_tap_passkey
+
+ CredentialType.PASSWORD ->
+ R.string.get_dialog_description_single_tap_password
+
+ CredentialType.UNKNOWN ->
+ R.string.get_dialog_description_single_tap_saved_sign_in
+ },
getRequestDisplayInfo.appName,
username
)
+
return BiometricDisplayInfo(providerIcon = icon, providerName = providerName,
displayTitleText = displayTitleText, descriptionForCredential = descriptionText,
biometricRequestInfo = selectedEntry.biometricRequest as BiometricRequestInfo)
@@ -373,7 +504,7 @@
* if this is called, a result is guaranteed. Specifically, this is guaranteed to return a non-null
* value unlike the get counterpart.
*/
-private fun createBiometricDisplayValues(
+private fun retrieveBiometricCreateDisplayValues(
createRequestDisplayInfo: com.android.credentialmanager.createflow.RequestDisplayInfo,
createProviderInfo: EnabledProviderInfo,
context: Context,
@@ -388,23 +519,12 @@
getCreateTitleResCode(createRequestDisplayInfo),
createRequestDisplayInfo.appName
)
- val descriptionText: String = context.getString(
- when (createRequestDisplayInfo.type) {
- CredentialType.PASSKEY ->
- R.string.choose_create_single_tap_passkey_title
- CredentialType.PASSWORD ->
- R.string.choose_create_single_tap_password_title
-
- CredentialType.UNKNOWN ->
- R.string.choose_create_single_tap_sign_in_title
- },
- createRequestDisplayInfo.appName,
- )
- // TODO(b/327620327) : Add a subtitle and any other recently aligned ideas
+ // TODO(b/330396140) : If footerDescription is null, determine if we need to fallback
return BiometricDisplayInfo(providerIcon = icon, providerName = providerName,
- displayTitleText = displayTitleText, descriptionForCredential = descriptionText,
- biometricRequestInfo = selectedEntry.biometricRequest as BiometricRequestInfo)
+ displayTitleText = displayTitleText, descriptionForCredential = selectedEntry
+ .footerDescription, biometricRequestInfo = selectedEntry.biometricRequest
+ as BiometricRequestInfo, displaySubtitleText = createRequestDisplayInfo.title)
}
/**
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricPromptState.kt
similarity index 63%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt
rename to packages/CredentialManager/src/com/android/credentialmanager/common/BiometricPromptState.kt
index 979d8e7..e1aa041 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricPromptState.kt
@@ -14,9 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.scene.shared.flag
+package com.android.credentialmanager.common
-import com.android.systemui.kosmos.Kosmos
-
-var Kosmos.fakeSceneContainerFlags by Kosmos.Fixture { FakeSceneContainerFlags() }
-val Kosmos.sceneContainerFlags by Kosmos.Fixture<SceneContainerFlags> { fakeSceneContainerFlags }
+enum class BiometricPromptState {
+ /** The biometric prompt hasn't been activated. */
+ INACTIVE,
+ /** The biometric prompt is active but data hasn't been returned yet. */
+ PENDING,
+ /** The biometric prompt has closed and returned data we then send to the provider activity. */
+ COMPLETE
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
index 7e7a74f..3c80113 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
@@ -22,7 +22,9 @@
const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
"androidx.credentials.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED"
const val IS_AUTO_SELECTED_KEY = "IS_AUTO_SELECTED"
- const val BIOMETRIC_AUTH_TYPE = "BIOMETRIC_AUTH_TYPE"
- const val BIOMETRIC_AUTH_FAILURE = "BIOMETRIC_AUTH_FAILURE"
+ // TODO(b/333445772) : Qualify error codes fully for propagation
+ const val BIOMETRIC_AUTH_RESULT = "BIOMETRIC_AUTH_RESULT"
+ const val BIOMETRIC_AUTH_ERROR_CODE = "BIOMETRIC_AUTH_ERROR_CODE"
+ const val BIOMETRIC_AUTH_ERROR_MESSAGE = "BIOMETRIC_AUTH_ERROR_MESSAGE"
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
index d13d86f..149c14a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
@@ -349,6 +349,38 @@
}
}
+@Composable
+fun MoreOptionTopAppBarWithCustomNavigation(
+ text: String,
+ onNavigationIconClicked: () -> Unit,
+ navigationIcon: ImageVector,
+ navigationIconContentDescription: String,
+ bottomPadding: Dp,
+) {
+ Row(
+ modifier = Modifier.padding(top = 12.dp, bottom = bottomPadding),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ IconButton(
+ modifier = Modifier.padding(top = 8.dp, bottom = 8.dp, start = 4.dp).size(48.dp),
+ onClick = onNavigationIconClicked
+ ) {
+ Box(
+ modifier = Modifier.size(48.dp),
+ contentAlignment = Alignment.Center,
+ ) {
+ Icon(
+ imageVector = navigationIcon,
+ contentDescription = navigationIconContentDescription,
+ modifier = Modifier.size(24.dp).autoMirrored(),
+ tint = LocalAndroidColorScheme.current.onSurfaceVariant,
+ )
+ }
+ }
+ LargeTitleText(text = text, modifier = Modifier.padding(horizontal = 4.dp))
+ }
+}
+
private fun Modifier.autoMirrored() = composed {
when (LocalLayoutDirection.current) {
LayoutDirection.Rtl -> graphicsLayer(scaleX = -1f)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 25fb477..a0915d2 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -46,11 +46,14 @@
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
+import com.android.credentialmanager.common.BiometricError
+import com.android.credentialmanager.common.BiometricFlowType
+import com.android.credentialmanager.common.BiometricPromptState
import com.android.credentialmanager.model.EntryInfo
import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
-import com.android.credentialmanager.common.runBiometricFlow
+import com.android.credentialmanager.common.runBiometricFlowForCreate
import com.android.credentialmanager.common.ui.ActionButton
import com.android.credentialmanager.common.ui.BodyMediumText
import com.android.credentialmanager.common.ui.BodySmallText
@@ -111,7 +114,11 @@
onBiometricEntrySelected =
viewModel::createFlowOnEntrySelected,
fallbackToOriginalFlow =
- viewModel::getFlowOnBackToPrimarySelectionScreen,
+ viewModel::fallbackFromBiometricToNormalFlow,
+ getBiometricPromptState =
+ viewModel::getBiometricPromptState,
+ onBiometricPromptStateChange =
+ viewModel::onBiometricPromptStateChange
)
CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard(
requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
@@ -575,21 +582,29 @@
onMoreOptionSelected: () -> Unit,
requestDisplayInfo: RequestDisplayInfo,
enabledProviderInfo: EnabledProviderInfo,
- onBiometricEntrySelected: (EntryInfo, BiometricPrompt.AuthenticationResult) -> Unit,
+ onBiometricEntrySelected: (
+ EntryInfo,
+ BiometricPrompt.AuthenticationResult?,
+ BiometricError?
+ ) -> Unit,
onCancelFlowAndFinish: () -> Unit,
onIllegalScreenStateAndFinish: (String) -> Unit,
- fallbackToOriginalFlow: () -> Unit,
+ fallbackToOriginalFlow: (BiometricFlowType) -> Unit,
+ getBiometricPromptState: () -> BiometricPromptState,
+ onBiometricPromptStateChange: (BiometricPromptState) -> Unit,
) {
if (biometricEntry == null) {
- fallbackToOriginalFlow()
+ fallbackToOriginalFlow(BiometricFlowType.CREATE)
return
}
- runBiometricFlow(
+ runBiometricFlowForCreate(
biometricEntry = biometricEntry,
context = LocalContext.current,
openMoreOptionsPage = onMoreOptionSelected,
sendDataToProvider = onBiometricEntrySelected,
onCancelFlowAndFinish = onCancelFlowAndFinish,
+ getBiometricPromptState = getBiometricPromptState,
+ onBiometricPromptStateChange = onBiometricPromptStateChange,
createRequestDisplayInfo = requestDisplayInfo,
createProviderInfo = enabledProviderInfo,
onBiometricFailureFallback = fallbackToOriginalFlow,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index 1d262ba..ddd4139 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -78,13 +78,8 @@
*/
internal fun isBiometricFlow(
activeEntry: ActiveEntry,
- sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
- requestDisplayInfo: RequestDisplayInfo,
-) = findBiometricFlowEntry(activeEntry, isFlowAutoSelectable(
- requestDisplayInfo = requestDisplayInfo,
- activeEntry = activeEntry,
- sortedCreateOptionsPairs = sortedCreateOptionsPairs
-)) != null
+ isAutoSelectFlow: Boolean,
+) = findBiometricFlowEntry(activeEntry, isAutoSelectFlow) != null
/**
* This utility presents the correct resource string for the create flows title conditionally.
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 6d1a3dd..ce4f402 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -32,6 +32,7 @@
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.outlined.QrCodeScanner
import androidx.compose.material3.Divider
import androidx.compose.material3.TextButton
@@ -52,9 +53,12 @@
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
+import com.android.credentialmanager.common.BiometricError
+import com.android.credentialmanager.common.BiometricFlowType
+import com.android.credentialmanager.common.BiometricPromptState
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
-import com.android.credentialmanager.common.runBiometricFlow
+import com.android.credentialmanager.common.runBiometricFlowForGet
import com.android.credentialmanager.common.ui.ActionButton
import com.android.credentialmanager.common.ui.ActionEntry
import com.android.credentialmanager.common.ui.ConfirmButton
@@ -67,6 +71,7 @@
import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
import com.android.credentialmanager.common.ui.ModalBottomSheet
import com.android.credentialmanager.common.ui.MoreOptionTopAppBar
+import com.android.credentialmanager.common.ui.MoreOptionTopAppBarWithCustomNavigation
import com.android.credentialmanager.common.ui.SheetContainerCard
import com.android.credentialmanager.common.ui.Snackbar
import com.android.credentialmanager.common.ui.SnackbarActionText
@@ -145,7 +150,7 @@
.currentScreenState == GetScreenState.BIOMETRIC_SELECTION) {
BiometricSelectionPage(
biometricEntry = getCredentialUiState.activeEntry,
- onMoreOptionSelected = viewModel::getFlowOnMoreOptionSelected,
+ onMoreOptionSelected = viewModel::getFlowOnMoreOptionOnlySelected,
onCancelFlowAndFinish = viewModel::onUserCancel,
onIllegalStateAndFinish = viewModel::onIllegalUiState,
requestDisplayInfo = getCredentialUiState.requestDisplayInfo,
@@ -154,8 +159,34 @@
onBiometricEntrySelected =
viewModel::getFlowOnEntrySelected,
fallbackToOriginalFlow =
- viewModel::getFlowOnBackToPrimarySelectionScreen,
+ viewModel::fallbackFromBiometricToNormalFlow,
+ getBiometricPromptState =
+ viewModel::getBiometricPromptState,
+ onBiometricPromptStateChange =
+ viewModel::onBiometricPromptStateChange
)
+ } else if (credmanBiometricApiEnabled() &&
+ getCredentialUiState.currentScreenState
+ == GetScreenState.ALL_SIGN_IN_OPTIONS_ONLY) {
+ AllSignInOptionCard(
+ providerInfoList = getCredentialUiState.providerInfoList,
+ providerDisplayInfo = getCredentialUiState.providerDisplayInfo,
+ onEntrySelected = viewModel::getFlowOnEntrySelected,
+ onBackButtonClicked = viewModel::onUserCancel,
+ onCancel = viewModel::onUserCancel,
+ onLog = { viewModel.logUiEvent(it) },
+ customTopBar = { MoreOptionTopAppBarWithCustomNavigation(
+ text = stringResource(
+ R.string.get_dialog_title_sign_in_options),
+ onNavigationIconClicked = viewModel::onUserCancel,
+ navigationIcon = Icons.Filled.Close,
+ navigationIconContentDescription =
+ stringResource(R.string.accessibility_close_button),
+ bottomPadding = 0.dp
+ ) }
+ )
+ viewModel.uiMetrics.log(GetCredentialEvent
+ .CREDMAN_GET_CRED_SCREEN_ALL_SIGN_IN_OPTIONS)
} else {
AllSignInOptionCard(
providerInfoList = getCredentialUiState.providerInfoList,
@@ -217,20 +248,28 @@
requestDisplayInfo: RequestDisplayInfo,
providerInfoList: List<ProviderInfo>,
providerDisplayInfo: ProviderDisplayInfo,
- onBiometricEntrySelected: (EntryInfo, BiometricPrompt.AuthenticationResult?) -> Unit,
- fallbackToOriginalFlow: () -> Unit,
+ onBiometricEntrySelected: (
+ EntryInfo,
+ BiometricPrompt.AuthenticationResult?,
+ BiometricError?
+ ) -> Unit,
+ fallbackToOriginalFlow: (BiometricFlowType) -> Unit,
+ getBiometricPromptState: () -> BiometricPromptState,
+ onBiometricPromptStateChange: (BiometricPromptState) -> Unit,
) {
if (biometricEntry == null) {
- fallbackToOriginalFlow()
+ fallbackToOriginalFlow(BiometricFlowType.GET)
return
}
- runBiometricFlow(
+ runBiometricFlowForGet(
biometricEntry = biometricEntry,
context = LocalContext.current,
openMoreOptionsPage = onMoreOptionSelected,
sendDataToProvider = onBiometricEntrySelected,
onCancelFlowAndFinish = onCancelFlowAndFinish,
onIllegalStateAndFinish = onIllegalStateAndFinish,
+ getBiometricPromptState = getBiometricPromptState,
+ onBiometricPromptStateChange = onBiometricPromptStateChange,
getRequestDisplayInfo = requestDisplayInfo,
getProviderInfoList = providerInfoList,
getProviderDisplayInfo = providerDisplayInfo,
@@ -510,19 +549,8 @@
R.string.get_dialog_title_choose_password_for
else if (areAllPasskeysOnPrimaryScreen)
R.string.get_dialog_title_choose_passkey_for
- else if (primaryPageLockedEntryList.isNotEmpty() ||
- primaryPageCredentialEntryList.any {
- it.sortedCredentialEntryList.first().credentialType !=
- CredentialType.PASSWORD &&
- it.sortedCredentialEntryList.first().credentialType !=
- CredentialType.PASSKEY
- }
- ) // An unknown typed / locked entry exists, and we can't say it is
- // already saved, strictly speaking. Hence use a different title
- // without the mention of "saved".
+ else
R.string.get_dialog_title_choose_sign_in_for
- else // All entries on the primary screen are passkeys or passwords
- R.string.get_dialog_title_choose_saved_sign_in_for
},
requestDisplayInfo.appName
),
@@ -627,7 +655,13 @@
return providerId
}
-/** Draws the secondary credential selection page, where all sign-in options are listed. */
+/**
+ * Draws the secondary credential selection page, where all sign-in options are listed.
+ *
+ * By default, this card has 'back' navigation whereby user can navigate back to invoke
+ * [onBackButtonClicked]. However if a different top bar with possibly a different navigation
+ * is required, then the caller of this Composable can set a [customTopBar].
+ */
@Composable
fun AllSignInOptionCard(
providerInfoList: List<ProviderInfo>,
@@ -636,16 +670,21 @@
onBackButtonClicked: () -> Unit,
onCancel: () -> Unit,
onLog: @Composable (UiEventEnum) -> Unit,
+ customTopBar: (@Composable() () -> Unit)? = null
) {
val sortedUserNameToCredentialEntryList =
providerDisplayInfo.sortedUserNameToCredentialEntryList
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
SheetContainerCard(topAppBar = {
- MoreOptionTopAppBar(
- text = stringResource(R.string.get_dialog_title_sign_in_options),
- onNavigationIconClicked = onBackButtonClicked,
- bottomPadding = 0.dp,
- )
+ if (customTopBar != null) {
+ customTopBar()
+ } else {
+ MoreOptionTopAppBar(
+ text = stringResource(R.string.get_dialog_title_sign_in_options),
+ onNavigationIconClicked = onBackButtonClicked,
+ bottomPadding = 0.dp,
+ )
+ }
}) {
var isFirstSection = true
// For username
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index ac776af..8e78861 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -163,7 +163,11 @@
/** The single tap biometric selection page. */
BIOMETRIC_SELECTION,
- /** The secondary credential selection page, where all sign-in options are listed. */
+ /**
+ * The secondary credential selection page, where all sign-in options are listed.
+ *
+ * This state is expected to go back to PRIMARY_SELECTION on back navigation
+ */
ALL_SIGN_IN_OPTIONS,
/** The snackbar only page when there's no account but only a remoteEntry. */
@@ -171,6 +175,14 @@
/** The snackbar when there are only auth entries and all of them turn out to be empty. */
UNLOCKED_AUTH_ENTRIES_ONLY,
+
+ /**
+ * The secondary credential selection page, where all sign-in options are listed.
+ *
+ * This state has no option for the user to navigate back to PRIMARY_SELECTION, and
+ * instead can be terminated independently.
+ */
+ ALL_SIGN_IN_OPTIONS_ONLY,
}
@@ -285,8 +297,8 @@
providerDisplayInfo.remoteEntry != null)
GetScreenState.REMOTE_ONLY
else if (isRequestForAllOptions)
- GetScreenState.ALL_SIGN_IN_OPTIONS
- else if (isBiometricFlow(providerDisplayInfo))
+ GetScreenState.ALL_SIGN_IN_OPTIONS_ONLY
+ else if (isBiometricFlow(providerDisplayInfo, isFlowAutoSelectable(providerDisplayInfo)))
GetScreenState.BIOMETRIC_SELECTION
else GetScreenState.PRIMARY_SELECTION
}
@@ -294,9 +306,14 @@
/**
* Determines if the flow is a biometric flow by taking into account autoselect criteria.
*/
-internal fun isBiometricFlow(providerDisplayInfo: ProviderDisplayInfo) =
- findBiometricFlowEntry(providerDisplayInfo,
- findAutoSelectEntry(providerDisplayInfo) != null) != null
+internal fun isBiometricFlow(providerDisplayInfo: ProviderDisplayInfo, isAutoSelectFlow: Boolean) =
+ findBiometricFlowEntry(providerDisplayInfo, isAutoSelectFlow) != null
+
+/**
+ * Determines if the flow is an autoselect flow.
+ */
+internal fun isFlowAutoSelectable(providerDisplayInfo: ProviderDisplayInfo) =
+ findAutoSelectEntry(providerDisplayInfo) != null
internal class CredentialEntryInfoComparatorByTypeThenTimestamp(
val typePriorityMap: Map<String, Int>,
diff --git a/packages/CredentialManager/wear/res/drawable/passkey_icon.xml b/packages/CredentialManager/wear/res/drawable/passkey_icon.xml
deleted file mode 100644
index be366bf..0000000
--- a/packages/CredentialManager/wear/res/drawable/passkey_icon.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<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="M23,10.5H17V13.5H23V10.5Z"
- android:fillColor="#188038"/>
- <path
- android:pathData="M6.5,17.5C3.5,17.5 1,15 1,12C1,9 3.5,6.5 6.5,6.5C9.5,6.5 12,9 12,12C12,15 9.5,17.5 6.5,17.5ZM6.5,9.5C5.1,9.5 4,10.6 4,12C4,13.4 5.1,14.5 6.5,14.5C7.9,14.5 9,13.4 9,12C9,10.6 7.9,9.5 6.5,9.5Z"
- android:fillColor="#4285F4"/>
- <path
- android:pathData="M21,13.5H19H17V16.5H19V15.5C19,14.9 19.4,14.5 20,14.5C20.6,14.5 21,14.9 21,15.5V16.5H23V13.5H21Z"
- android:fillColor="#34A853"/>
- <path
- android:pathData="M11.8,10.5H8.5C8.8,10.9 9,11.4 9,12C9,12.6 8.8,13.1 8.5,13.5H11.8C11.9,13 12,12.5 12,12C12,11.5 11.9,11 11.8,10.5Z"
- android:fillColor="#EA4335"/>
- <path
- android:pathData="M17,10.5H11.8C11.9,11 12,11.5 12,12C12,12.5 11.9,13 11.8,13.5H17V10.5Z"
- android:fillColor="#FBBC04"/>
-</vector>
diff --git a/packages/CredentialManager/wear/res/values-watch/donottranslate.xml b/packages/CredentialManager/wear/res/values-watch/donottranslate.xml
new file mode 100644
index 0000000..c3ab3cb
--- /dev/null
+++ b/packages/CredentialManager/wear/res/values-watch/donottranslate.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- font-family-device-default is expected to be preloaded in the font_customization.xml(/vendor/<OEM>/products/<PRODUCT>/fonts/fonts_customization.xml)-->
+ <!-- Falls back to system default when font-family-device-default doesn't exist -->
+ <string name="wear_material_compose_display_1_font_family">font-family-device-default</string>
+ <string name="wear_material_compose_display_2_font_family">font-family-device-default</string>
+ <string name="wear_material_compose_display_3_font_family">font-family-device-default</string>
+ <string name="wear_material_compose_title_1_font_family">font-family-medium-device-default</string>
+ <string name="wear_material_compose_title_2_font_family">font-family-medium-device-default</string>
+ <string name="wear_material_compose_title_3_font_family">font-family-device-default</string>
+ <string name="wear_material_compose_body_1_font_family">font-family-text-device-default</string>
+ <string name="wear_material_compose_body_2_font_family">font-family-text-device-default</string>
+ <string name="wear_material_compose_button_font_family">font-family-text-medium-device-default</string>
+ <string name="wear_material_compose_caption_1_font_family">font-family-text-medium-device-default</string>
+ <string name="wear_material_compose_caption_2_font_family">font-family-text-medium-device-default</string>
+ <string name="wear_material_compose_caption_3_font_family">font-family-text-medium-device-default</string>
+</resources>
diff --git a/packages/CredentialManager/wear/res/values/overlayable.xml b/packages/CredentialManager/wear/res/values/overlayable.xml
new file mode 100644
index 0000000..5b9d372
--- /dev/null
+++ b/packages/CredentialManager/wear/res/values/overlayable.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <overlayable name="CredentialSelectorStyles">
+ <policy type="product|system|vendor|odm|oem">
+ <!--START WEAR SPECIFIC FONT STRINGS -->
+ <item type="string" name="wear_material_compose_display_1_font_family" />
+ <item type="string" name="wear_material_compose_display_2_font_family" />
+ <item type="string" name="wear_material_compose_display_3_font_family" />
+ <item type="string" name="wear_material_compose_title_1_font_family" />
+ <item type="string" name="wear_material_compose_title_2_font_family" />
+ <item type="string" name="wear_material_compose_title_3_font_family" />
+ <item type="string" name="wear_material_compose_body_1_font_family" />
+ <item type="string" name="wear_material_compose_body_2_font_family" />
+ <item type="string" name="wear_material_compose_button_font_family" />
+ <item type="string" name="wear_material_compose_caption_1_font_family" />
+ <item type="string" name="wear_material_compose_caption_2_font_family" />
+ <item type="string" name="wear_material_compose_caption_3_font_family" />
+ <!--END WEAR SPECIFIC FONT STRINGS -->
+
+ </policy>
+
+ </overlayable>
+
+</resources>
diff --git a/packages/CredentialManager/wear/robotests/src/com/android/credentialmanager/CredentialSelectorUiStateGetMapperTest.kt b/packages/CredentialManager/wear/robotests/src/com/android/credentialmanager/CredentialSelectorUiStateGetMapperTest.kt
index 3422d3d..c6013e2 100644
--- a/packages/CredentialManager/wear/robotests/src/com/android/credentialmanager/CredentialSelectorUiStateGetMapperTest.kt
+++ b/packages/CredentialManager/wear/robotests/src/com/android/credentialmanager/CredentialSelectorUiStateGetMapperTest.kt
@@ -65,29 +65,29 @@
isLastUnlocked = true
)
- val passkeyCredentialEntryInfo =
+ private val passkeyCredentialEntryInfo =
createCredentialEntryInfo(credentialType = CredentialType.PASSKEY, userName = "userName")
- val unknownCredentialEntryInfo =
+ private val unknownCredentialEntryInfo =
createCredentialEntryInfo(credentialType = CredentialType.UNKNOWN, userName = "userName2")
- val passwordCredentialEntryInfo =
+ private val passwordCredentialEntryInfo =
createCredentialEntryInfo(credentialType = CredentialType.PASSWORD, userName = "userName")
- val recentlyUsedPasskeyCredential =
+ private val recentlyUsedPasskeyCredential =
createCredentialEntryInfo(credentialType =
CredentialType.PASSKEY, lastUsedTimeMillis = 2L, userName = "userName")
- val recentlyUsedPasswordCredential =
+ private val recentlyUsedPasswordCredential =
createCredentialEntryInfo(credentialType =
CredentialType.PASSWORD, lastUsedTimeMillis = 2L, userName = "userName")
- val credentialList1 = listOf(
+ private val credentialList1 = listOf(
passkeyCredentialEntryInfo,
passwordCredentialEntryInfo
)
- val credentialList2 = listOf(
+ private val credentialList2 = listOf(
passkeyCredentialEntryInfo,
passwordCredentialEntryInfo,
recentlyUsedPasskeyCredential,
@@ -100,7 +100,6 @@
val getCredentialUiState = Request.Get(
token = null,
resultReceiver = null,
- finalResponseReceiver = null,
providerInfos = listOf(createProviderInfo(credentialList1))).toGet(isPrimary = true)
assertThat(getCredentialUiState).isEqualTo(
@@ -113,16 +112,16 @@
val getCredentialUiState = Request.Get(
token = null,
resultReceiver = null,
- finalResponseReceiver = null,
providerInfos = listOf(createProviderInfo(listOf(passkeyCredentialEntryInfo,
unknownCredentialEntryInfo)))).toGet(isPrimary = true)
assertThat(getCredentialUiState).isEqualTo(
- CredentialSelectorUiState.Get.SingleEntryPerAccount(
+ CredentialSelectorUiState.Get.MultipleEntryPrimaryScreen(
sortedEntries = listOf(
passkeyCredentialEntryInfo, // userName
unknownCredentialEntryInfo // userName2
),
+ icon = mDrawable,
authenticationEntryList = listOf(authenticationEntryInfo)
)) // prefer passkey from account 1, then unknown from account 2
}
@@ -132,7 +131,6 @@
val getCredentialUiState = Request.Get(
token = null,
resultReceiver = null,
- finalResponseReceiver = null,
providerInfos = listOf(createProviderInfo(credentialList1))).toGet(isPrimary = false)
assertThat(getCredentialUiState).isEqualTo(
@@ -151,7 +149,6 @@
val getCredentialUiState = Request.Get(
token = null,
resultReceiver = null,
- finalResponseReceiver = null,
providerInfos = listOf(createProviderInfo(credentialList1),
createProviderInfo(credentialList2))).toGet(isPrimary = false)
diff --git a/packages/CredentialManager/wear/robotests/src/com/android/credentialmanager/CredentialSelectorViewModelTest.kt b/packages/CredentialManager/wear/robotests/src/com/android/credentialmanager/CredentialSelectorViewModelTest.kt
index b79f34c..cf839f8 100644
--- a/packages/CredentialManager/wear/robotests/src/com/android/credentialmanager/CredentialSelectorViewModelTest.kt
+++ b/packages/CredentialManager/wear/robotests/src/com/android/credentialmanager/CredentialSelectorViewModelTest.kt
@@ -121,7 +121,6 @@
stateFlow.value = Request.Get(
token = null,
resultReceiver = null,
- finalResponseReceiver = null,
providerInfos = emptyList())
mViewModel.back()
@@ -136,7 +135,6 @@
stateFlow.value = Request.Get(
token = null,
resultReceiver = null,
- finalResponseReceiver = null,
providerInfos = emptyList())
mViewModel.back()
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 0fe35e6..652e62c 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -21,7 +21,7 @@
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
-import androidx.wear.compose.material.MaterialTheme
+import com.android.credentialmanager.ui.theme.WearCredentialSelectorTheme
import com.android.credentialmanager.ui.WearApp
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import dagger.hilt.android.AndroidEntryPoint
@@ -36,7 +36,7 @@
super.onCreate(savedInstanceState)
setTheme(android.R.style.Theme_DeviceDefault)
setContent {
- MaterialTheme {
+ WearCredentialSelectorTheme {
WearApp(
flowEngine = viewModel,
onCloseApp = { finish() },
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index b7fa33e..3608568 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -86,7 +86,7 @@
when (uiState.value) {
is Get.MultipleEntry -> isPrimaryScreen.value = true
is Create, Close, is Cancel, Idle -> shouldClose.value = true
- is Get.SingleEntry, is Get.SingleEntryPerAccount -> cancel()
+ is Get.SingleEntry, is Get.MultipleEntryPrimaryScreen -> cancel()
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt
index c05fc93..b2f55c1 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt
@@ -17,6 +17,7 @@
package com.android.credentialmanager
import android.content.Intent
+import android.graphics.drawable.Drawable
import androidx.activity.result.IntentSenderRequest
import androidx.compose.runtime.Composable
import com.android.credentialmanager.model.EntryInfo
@@ -71,14 +72,14 @@
/** Getting credential UI state when there is only one credential available. */
data class SingleEntry(val entry: CredentialEntryInfo) : Get()
/**
- * Getting credential UI state when there is only one account while with multiple
- * credentials, with different types(eg, passkey vs password) or providers.
+ * Getting credential UI state on primary screen when there is are multiple accounts.
*/
- data class SingleEntryPerAccount(
+ data class MultipleEntryPrimaryScreen(
+ val icon: Drawable?,
val sortedEntries: List<CredentialEntryInfo>,
val authenticationEntryList: List<AuthenticationEntryInfo>,
) : Get()
- /** Getting credential UI state when there are multiple accounts available. */
+ /** Getting credential UI state on secondary screen when there are multiple accounts available. */
data class MultipleEntry(
val accounts: List<PerUserNameEntries>,
val actionEntryList: List<ActionEntryInfo>,
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
index 018db68..a75aeaf 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -29,7 +29,7 @@
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
import com.android.credentialmanager.CredentialSelectorUiState
-import com.android.credentialmanager.CredentialSelectorUiState.Get.SingleEntryPerAccount
+import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntryPrimaryScreen
import com.android.credentialmanager.CredentialSelectorUiState.Get.SingleEntry
import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry
import com.android.credentialmanager.FlowEngine
@@ -95,7 +95,7 @@
scrollable(Screen.MultipleCredentialsScreenFold.route) {
MultiCredentialsFoldScreen(
- credentialSelectorUiState = (remember { uiState } as SingleEntryPerAccount),
+ credentialSelectorUiState = (remember { uiState } as MultipleEntryPrimaryScreen),
columnState = it.columnState,
flowEngine = flowEngine,
)
@@ -124,7 +124,6 @@
handleGetNavigation(
navController = navController,
state = state,
- onCloseApp = onCloseApp,
selectEntry = selectEntry
)
}
@@ -147,7 +146,6 @@
private fun handleGetNavigation(
navController: NavController,
state: CredentialSelectorUiState.Get,
- onCloseApp: () -> Unit,
selectEntry: (entry: EntryInfo, isAutoSelected: Boolean) -> Unit,
) {
when (state) {
@@ -169,7 +167,7 @@
}
}
- is SingleEntryPerAccount -> {
+ is MultipleEntryPrimaryScreen -> {
navController.navigateToMultipleCredentialsFoldScreen()
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/AccountRow.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/AccountRow.kt
index 8b19e1b..3088fed 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/AccountRow.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/AccountRow.kt
@@ -21,35 +21,25 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.material.MaterialTheme
-import androidx.wear.compose.material.Text
+import com.android.credentialmanager.common.ui.components.WearDisplayNameText
+import com.android.credentialmanager.common.ui.components.WearUsernameText
import com.google.android.horologist.compose.tools.WearPreview
@Composable
fun AccountRow(
primaryText: String,
secondaryText: String? = null,
- modifier: Modifier = Modifier,
) {
- Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
- Text(
+ Column(modifier = Modifier.padding(bottom = 12.dp),
+ horizontalAlignment = Alignment.CenterHorizontally) {
+ WearDisplayNameText(
text = primaryText,
- color = Color(0xFFE6FF7B),
- overflow = TextOverflow.Ellipsis,
- maxLines = 1,
- style = MaterialTheme.typography.title2
)
if (secondaryText != null) {
- Text(
+ WearUsernameText(
text = secondaryText,
- modifier = Modifier.padding(top = 7.dp),
- color = Color(0xFFCAC5BC),
- overflow = TextOverflow.Ellipsis,
- maxLines = 2,
- style = MaterialTheme.typography.body1,
+ modifier = Modifier.padding(top = 8.dp)
)
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
index 8e5a866..c641d7f 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
@@ -22,12 +22,9 @@
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.Chip
@@ -35,11 +32,12 @@
import androidx.wear.compose.material.ChipColors
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.style.TextAlign
import androidx.wear.compose.material.ChipDefaults
-import androidx.wear.compose.material.Text
import com.android.credentialmanager.R
+import com.android.credentialmanager.common.ui.components.WearButtonText
+import com.android.credentialmanager.common.ui.components.WearSecondaryLabel
import com.android.credentialmanager.model.get.AuthenticationEntryInfo
-import com.android.credentialmanager.ui.components.CredentialsScreenChip.TOPPADDING
/* Used as credential suggestion or user action chip. */
@Composable
@@ -49,36 +47,61 @@
secondaryLabel: String? = null,
icon: Drawable? = null,
isAuthenticationEntryLocked: Boolean = false,
+ textAlign: TextAlign = TextAlign.Center,
modifier: Modifier = Modifier,
- colors: ChipColors = ChipDefaults.secondaryChipColors(),
+ colors: ChipColors = ChipDefaults.secondaryChipColors()
) {
+ return CredentialsScreenChip(
+ onClick,
+ text = {
+ WearButtonText(
+ text = label,
+ textAlign = textAlign,
+ maxLines = if (secondaryLabel != null) 1 else 2
+ )
+ },
+ secondaryLabel,
+ icon,
+ isAuthenticationEntryLocked,
+ modifier,
+ colors
+ )
+}
+
+
+
+/* Used as credential suggestion or user action chip. */
+@Composable
+fun CredentialsScreenChip(
+ onClick: () -> Unit,
+ text: @Composable () -> Unit,
+ secondaryLabel: String? = null,
+ icon: Drawable? = null,
+ isAuthenticationEntryLocked: Boolean = false,
+ modifier: Modifier = Modifier,
+ colors: ChipColors = ChipDefaults.primaryChipColors(),
+ ) {
val labelParam: (@Composable RowScope.() -> Unit) =
{
- Text(
- text = label,
- overflow = TextOverflow.Ellipsis,
- maxLines = if (secondaryLabel != null) 1 else 2,
- )
+ text()
}
val secondaryLabelParam: (@Composable RowScope.() -> Unit)? =
secondaryLabel?.let {
{
Row {
- Text(
+ WearSecondaryLabel(
text = secondaryLabel,
- overflow = TextOverflow.Ellipsis,
- maxLines = 1,
)
if (isAuthenticationEntryLocked)
- // TODO(b/324465527) change this to lock icon and correct size once figma mocks are
- // updated
+ // TODO(b/324465527) change this to lock icon and correct size once figma mocks are
+ // updated
Icon(
bitmap = checkNotNull(icon?.toBitmap()?.asImageBitmap()),
// Decorative purpose only.
contentDescription = null,
- modifier = Modifier.size(20.dp),
+ modifier = Modifier.size(10.dp),
tint = Color.Unspecified
)
}
@@ -92,7 +115,7 @@
bitmap = it,
// Decorative purpose only.
contentDescription = null,
- modifier = Modifier.size(32.dp),
+ modifier = Modifier.size(24.dp),
tint = Color.Unspecified
)
}
@@ -117,9 +140,6 @@
onClick = { },
secondaryLabel = "beckett_bakery@gmail.com",
icon = null,
- modifier = Modifier
- .clipToBounds()
- .padding(top = 2.dp)
)
}
@@ -127,9 +147,8 @@
fun SignInOptionsChip(onClick: () -> Unit) {
CredentialsScreenChip(
label = stringResource(R.string.dialog_sign_in_options_button),
+ textAlign = TextAlign.Start,
onClick = onClick,
- modifier = Modifier
- .padding(top = TOPPADDING)
)
}
@@ -142,10 +161,13 @@
@Composable
fun ContinueChip(onClick: () -> Unit) {
CredentialsScreenChip(
- label = stringResource(R.string.dialog_continue_button),
onClick = onClick,
- modifier = Modifier
- .padding(top = TOPPADDING),
+ text = {
+ WearButtonText(
+ text = stringResource(R.string.dialog_continue_button),
+ textAlign = TextAlign.Center,
+ )
+ },
colors = ChipDefaults.primaryChipColors(),
)
}
@@ -161,21 +183,8 @@
CredentialsScreenChip(
label = stringResource(R.string.dialog_dismiss_button),
onClick = onClick,
- modifier = Modifier
- .padding(top = TOPPADDING),
)
}
-
-@Composable
-fun SignInOnPhoneChip(onClick: () -> Unit) {
- CredentialsScreenChip(
- label = stringResource(R.string.sign_in_on_phone_button),
- onClick = onClick,
- modifier = Modifier
- .padding(top = TOPPADDING),
- )
-}
-
@Composable
fun LockedProviderChip(
authenticationEntryInfo: AuthenticationEntryInfo,
@@ -191,9 +200,9 @@
label = authenticationEntryInfo.title,
icon = authenticationEntryInfo.icon,
secondaryLabel = secondaryLabel,
+ textAlign = TextAlign.Start,
isAuthenticationEntryLocked = !authenticationEntryInfo.isUnlockedAndEmpty,
onClick = onClick,
- modifier = Modifier.padding(top = TOPPADDING),
)
}
@@ -203,7 +212,3 @@
DismissChip({})
}
-private object CredentialsScreenChip {
- val TOPPADDING = 8.dp
-}
-
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/PasswordRow.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/PasswordRow.kt
index 97900b7..62e1c85 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/PasswordRow.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/PasswordRow.kt
@@ -21,33 +21,22 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.material.MaterialTheme
-import androidx.wear.compose.material.Text
+import com.android.credentialmanager.common.ui.components.WearDisplayNameText
+import com.android.credentialmanager.common.ui.components.WearUsernameText
import com.google.android.horologist.compose.tools.WearPreview
@Composable
fun PasswordRow(
email: String,
- modifier: Modifier = Modifier,
) {
- Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
- Text(
+ Column(modifier = Modifier.padding(bottom = 12.dp),
+ horizontalAlignment = Alignment.CenterHorizontally) {
+ WearDisplayNameText(
text = email,
- color = Color(0xFFE6FF7B),
- overflow = TextOverflow.Ellipsis,
- maxLines = 2,
- style = MaterialTheme.typography.title2
)
- Text(
- text = "••••••••••••••",
- modifier = Modifier.padding(top = 7.dp),
- color = Color(0xFFCAC5BC),
- overflow = TextOverflow.Ellipsis,
- maxLines = 1,
- style = MaterialTheme.typography.body1,
+ WearUsernameText(
+ text = "••••••••••••••"
)
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt
index 423662c..0afef5e 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt
@@ -18,49 +18,44 @@
import android.graphics.drawable.Drawable
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
-import androidx.wear.compose.material.Text
-import androidx.compose.ui.graphics.Color
-import androidx.compose.material3.Icon
-import androidx.wear.compose.material.MaterialTheme as WearMaterialTheme
-import androidx.compose.ui.text.style.TextAlign
+import com.android.credentialmanager.common.ui.components.WearTitleText
/* Used as header across Credential Selector screens. */
@Composable
fun SignInHeader(
icon: Drawable?,
title: String,
- modifier: Modifier = Modifier,
) {
Column(
- modifier = modifier,
+ modifier = Modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
if (icon != null) {
Icon(
bitmap = icon.toBitmap().asImageBitmap(),
- modifier = Modifier.size(32.dp),
+ modifier = Modifier.size(24.dp),
// Decorative purpose only.
contentDescription = null,
tint = Color.Unspecified,
)
}
+ Spacer(modifier = Modifier.size(8.dp))
- Text(
+ WearTitleText(
text = title,
- textAlign = TextAlign.Center,
- modifier = Modifier
- .padding(top = 6.dp)
- .padding(horizontal = 10.dp),
- style = WearMaterialTheme.typography.title3
)
+
+ Spacer(modifier = Modifier.size(8.dp))
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Spacers.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Spacers.kt
new file mode 100644
index 0000000..c87f176
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Spacers.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.ui.components
+
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+/**
+ * Space beneath all elements of screen
+ */
+@Composable
+fun BottomSpacer() {
+ Spacer(modifier = Modifier.size(40.dp))
+ }
+
+/**
+ * Usual space between Credential Screen Chips
+ */
+@Composable
+fun CredentialsScreenChipSpacer() {
+ Spacer(modifier = Modifier.size(4.dp))
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
new file mode 100644
index 0000000..22f6bf0
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.common.ui.components
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.MaterialTheme as WearMaterialTheme
+
+@Composable
+fun WearTitleText(text: String, modifier: Modifier = Modifier) {
+ Text(
+ modifier = modifier.wrapContentSize(),
+ text = text,
+ color = WearMaterialTheme.colors.onSurface,
+ textAlign = TextAlign.Center,
+ style = WearMaterialTheme.typography.title3,
+ )
+}
+
+@Composable
+fun WearDisplayNameText(text: String, modifier: Modifier = Modifier) {
+ Text(
+ modifier = modifier.wrapContentSize(),
+ text = text,
+ color = WearMaterialTheme.colors.onSurfaceVariant,
+ textAlign = TextAlign.Center,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 2,
+ style = WearMaterialTheme.typography.title2,
+ )
+}
+
+@Composable
+fun WearUsernameText(
+ text: String,
+ textAlign: TextAlign = TextAlign.Center,
+ modifier: Modifier = Modifier,
+ onTextLayout: (TextLayoutResult) -> Unit = {},
+) {
+ Text(
+ modifier = modifier.padding(start = 8.dp, end = 8.dp).wrapContentSize(),
+ text = text,
+ color = WearMaterialTheme.colors.onSurfaceVariant,
+ style = WearMaterialTheme.typography.caption1,
+ overflow = TextOverflow.Ellipsis,
+ textAlign = textAlign,
+ maxLines = 2,
+ onTextLayout = onTextLayout,
+ )
+}
+
+// used for primary label in button
+@Composable
+fun WearButtonText(
+ text: String,
+ textAlign: TextAlign,
+ maxLines: Int = 1,
+ modifier: Modifier = Modifier,
+ color: Color = WearMaterialTheme.colors.onSurface,
+ onTextLayout: (TextLayoutResult) -> Unit = {},
+) {
+ Text(
+ modifier = modifier.wrapContentSize(),
+ text = text,
+ color = color,
+ style = WearMaterialTheme.typography.button,
+ overflow = TextOverflow.Ellipsis,
+ textAlign = textAlign,
+ maxLines = maxLines,
+ onTextLayout = onTextLayout,
+ )
+}
+
+@Composable
+fun WearSecondaryLabel(
+ text: String,
+ modifier: Modifier = Modifier,
+ onTextLayout: (TextLayoutResult) -> Unit = {},
+) {
+ Text(
+ modifier = modifier.wrapContentSize(),
+ text = text,
+ color = WearMaterialTheme.colors.onSurfaceVariant,
+ style = WearMaterialTheme.typography.caption1,
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Start,
+ maxLines = 1,
+ onTextLayout = onTextLayout,
+ )
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
index 7a936b6..0417533 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -16,6 +16,7 @@
package com.android.credentialmanager.ui.mappers
+import android.graphics.drawable.Drawable
import com.android.credentialmanager.model.Request
import com.android.credentialmanager.CredentialSelectorUiState
import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry.PerUserNameEntries
@@ -35,10 +36,19 @@
entry = accounts[0].value.minWith(comparator)
)
} else {
- CredentialSelectorUiState.Get.SingleEntryPerAccount(
- sortedEntries = accounts.map {
- it.value.minWith(comparator)
- }.sortedWith(comparator),
+ val sortedEntries = accounts.map {
+ it.value.minWith(comparator)
+ }.sortedWith(comparator)
+
+ var icon: Drawable? = null
+ // provide icon if all entries have the same provider
+ if (sortedEntries.all {it.providerId == sortedEntries[0].providerId}) {
+ icon = providerInfos[0].icon
+ }
+
+ CredentialSelectorUiState.Get.MultipleEntryPrimaryScreen(
+ sortedEntries = sortedEntries,
+ icon = icon,
authenticationEntryList = providerInfos.flatMap { it.authenticationEntryList }
)
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
index b3ab0c4..0b07643 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
@@ -17,12 +17,9 @@
package com.android.credentialmanager.ui.screens
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
@Composable
-fun LoadingScreen(
- modifier: Modifier = Modifier
-) {
+fun LoadingScreen() {
// Don't display anything, assuming that there should be minimal latency
// to parse the Credential Manager intent and define the state of the
// app. If latency is big, then a "loading" screen should be displayed
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
index d54103c..fb81e73 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
@@ -15,20 +15,21 @@
*/
package com.android.credentialmanager.ui.screens.multiple
+import com.android.credentialmanager.ui.components.CredentialsScreenChip
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.material.MaterialTheme
-import androidx.wear.compose.material.Text
-import com.android.credentialmanager.ui.components.SignInHeader
import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry
import com.android.credentialmanager.FlowEngine
import com.android.credentialmanager.R
+import com.android.credentialmanager.common.ui.components.WearButtonText
+import com.android.credentialmanager.common.ui.components.WearSecondaryLabel
import com.android.credentialmanager.model.get.CredentialEntryInfo
-import com.android.credentialmanager.ui.components.CredentialsScreenChip
+import com.android.credentialmanager.ui.components.CredentialsScreenChipSpacer
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.layout.ScalingLazyColumn
import com.google.android.horologist.compose.layout.ScalingLazyColumnState
@@ -55,20 +56,17 @@
) {
item {
// make this credential specific if all credentials are same
- SignInHeader(
- icon = null,
- title = stringResource(R.string.sign_in_options_title),
+ WearButtonText(
+ text = stringResource(R.string.sign_in_options_title),
+ textAlign = TextAlign.Start,
)
}
credentialSelectorUiState.accounts.forEach { userNameEntries ->
item {
- Text(
+ WearSecondaryLabel(
text = userNameEntries.userName,
- modifier = Modifier
- .padding(top = 6.dp)
- .padding(horizontal = 10.dp),
- style = MaterialTheme.typography.title3
+ modifier = Modifier.padding(top = 12.dp, bottom = 4.dp)
)
}
@@ -79,21 +77,20 @@
onClick = { selectEntry(credential, false) },
secondaryLabel = credential.credentialTypeDisplayName,
icon = credential.icon,
+ textAlign = TextAlign.Start
)
+
+ CredentialsScreenChipSpacer()
}
}
}
item {
- Text(
+ WearSecondaryLabel(
text = stringResource(R.string.provider_list_title),
- modifier = Modifier
- .padding(top = 6.dp)
- .padding(horizontal = 10.dp),
- style = MaterialTheme.typography.title3
+ modifier = Modifier.padding(top = 12.dp, bottom = 4.dp)
)
}
-
- credentialSelectorUiState.actionEntryList.forEach {actionEntry ->
+ credentialSelectorUiState.actionEntryList.forEach { actionEntry ->
item {
CredentialsScreenChip(
label = actionEntry.title,
@@ -101,9 +98,8 @@
secondaryLabel = null,
icon = actionEntry.icon,
)
+ CredentialsScreenChipSpacer()
}
}
}
}
-
-
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
index 6f32c99..7addc74 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
@@ -16,10 +16,11 @@
package com.android.credentialmanager.ui.screens.multiple
+import androidx.compose.foundation.layout.Spacer
import com.android.credentialmanager.R
import androidx.compose.ui.res.stringResource
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@@ -35,6 +36,8 @@
import com.google.android.horologist.compose.layout.ScalingLazyColumn
import com.google.android.horologist.compose.layout.ScalingLazyColumnState
import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.ui.components.BottomSpacer
+import com.android.credentialmanager.ui.components.CredentialsScreenChipSpacer
/**
* Screen that shows multiple credentials to select from.
@@ -45,7 +48,7 @@
@OptIn(ExperimentalHorologistApi::class)
@Composable
fun MultiCredentialsFoldScreen(
- credentialSelectorUiState: CredentialSelectorUiState.Get.SingleEntryPerAccount,
+ credentialSelectorUiState: CredentialSelectorUiState.Get.MultipleEntryPrimaryScreen,
columnState: ScalingLazyColumnState,
flowEngine: FlowEngine,
) {
@@ -58,42 +61,52 @@
val credentials = credentialSelectorUiState.sortedEntries
item {
var title = stringResource(R.string.choose_sign_in_title)
- if (credentials.all{ it.credentialType == CredentialType.PASSKEY }) {
+
+ if (credentials.isEmpty()) {
+ title = stringResource(R.string.choose_sign_in_title)
+ } else if (credentials.all{ it.credentialType == CredentialType.PASSKEY }) {
title = stringResource(R.string.choose_passkey_title)
} else if (credentials.all { it.credentialType == CredentialType.PASSWORD }) {
title = stringResource(R.string.choose_password_title)
}
SignInHeader(
- icon = null,
+ icon = credentialSelectorUiState.icon,
title = title,
- modifier = Modifier
- .padding(top = 6.dp),
)
}
credentials.forEach { credential: CredentialEntryInfo ->
- item {
- CredentialsScreenChip(
- label = credential.userName,
- onClick = { selectEntry(credential, false) },
- secondaryLabel = credential.credentialTypeDisplayName,
- icon = credential.icon,
- )
- }
+ item {
+ CredentialsScreenChip(
+ label = credential.userName,
+ onClick = { selectEntry(credential, false) },
+ secondaryLabel = credential.credentialTypeDisplayName,
+ icon = credential.icon,
+ )
+ CredentialsScreenChipSpacer()
}
+ }
credentialSelectorUiState.authenticationEntryList.forEach { authenticationEntryInfo ->
item {
LockedProviderChip(authenticationEntryInfo) {
- selectEntry(authenticationEntryInfo, false) }
+ selectEntry(authenticationEntryInfo, false)
+ }
+ CredentialsScreenChipSpacer()
}
}
+
+ item {
+ Spacer(modifier = Modifier.size(8.dp))
+ }
+
item {
SignInOptionsChip { flowEngine.openSecondaryScreen() }
}
item {
DismissChip { flowEngine.cancel() }
+ BottomSpacer()
}
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
index 56b1c2e..03608a4 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
@@ -29,17 +29,19 @@
import com.android.credentialmanager.R
import com.android.credentialmanager.ui.components.AccountRow
import com.android.credentialmanager.ui.components.ContinueChip
+import com.android.credentialmanager.ui.components.CredentialsScreenChipSpacer
import com.android.credentialmanager.ui.components.DismissChip
import com.android.credentialmanager.ui.components.SignInHeader
import com.android.credentialmanager.ui.components.SignInOptionsChip
import com.android.credentialmanager.ui.screens.single.SingleAccountScreen
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.android.credentialmanager.ui.components.BottomSpacer
/**
- * Screen that shows sign in with provider credential.
+ * Screen that shows single passkey credential.
*
- * @param entry The password entry
+ * @param entry The passkey entry
* @param columnState ScalingLazyColumn configuration to be be applied to SingleAccountScreen
* @param modifier styling for composable
* @param flowEngine [FlowEngine] that updates ui state for this screen
@@ -49,7 +51,6 @@
fun SinglePasskeyScreen(
entry: CredentialEntryInfo,
columnState: ScalingLazyColumnState,
- modifier: Modifier = Modifier,
flowEngine: FlowEngine,
) {
SingleAccountScreen(
@@ -60,21 +61,31 @@
)
},
accountContent = {
- AccountRow(
- primaryText = checkNotNull(entry.displayName),
- secondaryText = entry.userName,
- modifier = Modifier.padding(top = 10.dp),
+ val displayName = entry.displayName
+ if (displayName == null ||
+ entry.displayName.equals(entry.userName, ignoreCase = true)) {
+ AccountRow(
+ primaryText = entry.userName,
)
+ } else {
+ AccountRow(
+ primaryText = displayName,
+ secondaryText = entry.userName,
+ )
+ }
},
columnState = columnState,
- modifier = modifier.padding(horizontal = 10.dp)
+ modifier = Modifier.padding(horizontal = 10.dp)
) {
item {
val selectEntry = flowEngine.getEntrySelector()
Column {
ContinueChip { selectEntry(entry, false) }
+ CredentialsScreenChipSpacer()
SignInOptionsChip{ flowEngine.openSecondaryScreen() }
+ CredentialsScreenChipSpacer()
DismissChip { flowEngine.cancel() }
+ BottomSpacer()
}
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index 2ca8ef1..818723b 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -33,11 +33,13 @@
import com.android.credentialmanager.ui.components.SignInOptionsChip
import com.android.credentialmanager.ui.screens.single.SingleAccountScreen
import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.ui.components.BottomSpacer
+import com.android.credentialmanager.ui.components.CredentialsScreenChipSpacer
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.layout.ScalingLazyColumnState
/**
- * Screen that shows sign in with provider credential.
+ * Screen that shows password credential.
*
* @param entry The password entry.
* @param columnState ScalingLazyColumn configuration to be be applied to SingleAccountScreen
@@ -49,7 +51,6 @@
fun SinglePasswordScreen(
entry: CredentialEntryInfo,
columnState: ScalingLazyColumnState,
- modifier: Modifier = Modifier,
flowEngine: FlowEngine,
) {
val selectEntry = flowEngine.getEntrySelector()
@@ -63,17 +64,19 @@
accountContent = {
PasswordRow(
email = entry.userName,
- modifier = Modifier.padding(top = 10.dp),
)
},
columnState = columnState,
- modifier = modifier.padding(horizontal = 10.dp)
+ modifier = Modifier.padding(horizontal = 10.dp)
) {
item {
Column {
ContinueChip { selectEntry(entry, false) }
+ CredentialsScreenChipSpacer()
SignInOptionsChip{ flowEngine.openSecondaryScreen() }
+ CredentialsScreenChipSpacer()
DismissChip { flowEngine.cancel() }
+ BottomSpacer()
}
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderScreen.kt
index 3a86feb..34d6e97 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderScreen.kt
@@ -24,7 +24,9 @@
import com.android.credentialmanager.FlowEngine
import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.ui.components.AccountRow
+import com.android.credentialmanager.ui.components.BottomSpacer
import com.android.credentialmanager.ui.components.ContinueChip
+import com.android.credentialmanager.ui.components.CredentialsScreenChipSpacer
import com.android.credentialmanager.ui.components.DismissChip
import com.android.credentialmanager.ui.components.SignInHeader
import com.android.credentialmanager.ui.components.SignInOptionsChip
@@ -35,7 +37,7 @@
/**
* Screen that shows sign in with provider credential.
*
- * @param entry The password entry.
+ * @param entry The custom credential entry.
* @param columnState ScalingLazyColumn configuration to be be applied to SingleAccountScreen
* @param modifier styling for composable
* @param flowEngine [FlowEngine] that updates ui state for this screen
@@ -57,16 +59,15 @@
},
accountContent = {
val displayName = entry.displayName
- if (displayName != null) {
+ if (displayName == null ||
+ entry.displayName.equals(entry.userName, ignoreCase = true)) {
AccountRow(
- primaryText = displayName,
- secondaryText = entry.userName,
- modifier = Modifier.padding(top = 10.dp),
+ primaryText = entry.userName,
)
} else {
AccountRow(
- primaryText = entry.userName,
- modifier = Modifier.padding(top = 10.dp),
+ primaryText = displayName,
+ secondaryText = entry.userName,
)
}
},
@@ -77,8 +78,11 @@
val selectEntry = flowEngine.getEntrySelector()
Column {
ContinueChip { selectEntry(entry, false) }
+ CredentialsScreenChipSpacer()
SignInOptionsChip{ flowEngine.openSecondaryScreen() }
+ CredentialsScreenChipSpacer()
DismissChip { flowEngine.cancel() }
+ BottomSpacer()
}
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/theme/WearCredentialSelectorTheme.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/theme/WearCredentialSelectorTheme.kt
new file mode 100644
index 0000000..ee0ba7b
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/theme/WearCredentialSelectorTheme.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.credentialmanager.ui.theme
+
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.annotation.StringRes
+import androidx.wear.compose.material.Colors
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.font.DeviceFontFamilyName
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontFamily
+import androidx.wear.compose.material.Typography
+import androidx.wear.compose.material.MaterialTheme
+import com.android.credentialmanager.R
+import androidx.compose.ui.graphics.Color
+
+/** The Material 3 Theme Wrapper for Supporting RRO. */
+@Composable
+fun WearCredentialSelectorTheme(content: @Composable () -> Unit) {
+ val context = LocalContext.current
+ val colors =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ overlayColors(context)
+ .copy(error = MaterialTheme.colors.error, onError = MaterialTheme.colors.onError)
+ } else {
+ MaterialTheme.colors
+ }
+ MaterialTheme(colors = colors, typography = deviceDefaultTypography(context), content = content)
+}
+
+/**
+ * Creates a dynamic color maps that can be overlaid. 100 - Lightest shade; 0 - Darkest Shade; In
+ * wear we only support dark theme for the time being. Thus the fill colors and variants are dark
+ * and anything on top is light. We will use this custom redirection until wear compose material
+ * supports color scheming.
+ *
+ * The mapping is best case match on wear material color tokens from
+ * /android/clockwork/common/wearable/wearmaterial/color/res/values/color-tokens.xml
+ *
+ * @param context The context required to get system resource data.
+ */
+@RequiresApi(Build.VERSION_CODES.S)
+internal fun overlayColors(context: Context): Colors {
+ val tonalPalette = dynamicTonalPalette(context)
+ return Colors(
+ background = Color.Black,
+ onBackground = Color.White,
+ primary = tonalPalette.primary90,
+ primaryVariant = tonalPalette.primary80,
+ onPrimary = tonalPalette.primary10,
+ secondary = tonalPalette.tertiary90,
+ secondaryVariant = tonalPalette.tertiary60,
+ onSecondary = tonalPalette.tertiary10,
+ surface = tonalPalette.neutral20,
+ onSurface = tonalPalette.neutral95,
+ onSurfaceVariant = tonalPalette.neutralVariant80,
+ )
+}
+
+private fun fontFamily(context: Context, @StringRes id: Int): FontFamily {
+ val typefaceName = context.resources.getString(id)
+ val font = Font(familyName = DeviceFontFamilyName(typefaceName))
+ return FontFamily(font)
+}
+
+/*
+ Only customizes font family. The material 3 roles to 2.5 are mapped to the best case matching of
+ google3/java/com/google/android/wearable/libraries/compose/theme/GoogleMaterialTheme.kt
+*/
+internal fun deviceDefaultTypography(context: Context): Typography {
+ val defaultTypography = Typography()
+ return Typography(
+ display1 =
+ defaultTypography.display1.copy(
+ fontFamily =
+ fontFamily(context, R.string.wear_material_compose_display_1_font_family)
+ ),
+ display2 =
+ defaultTypography.display2.copy(
+ fontFamily =
+ fontFamily(context, R.string.wear_material_compose_display_2_font_family)
+ ),
+ display3 =
+ defaultTypography.display1.copy(
+ fontFamily =
+ fontFamily(context, R.string.wear_material_compose_display_3_font_family)
+ ),
+ title1 =
+ defaultTypography.title1.copy(
+ fontFamily = fontFamily(context, R.string.wear_material_compose_title_1_font_family)
+ ),
+ title2 =
+ defaultTypography.title2.copy(
+ fontFamily = fontFamily(context, R.string.wear_material_compose_title_2_font_family)
+ ),
+ title3 =
+ defaultTypography.title3.copy(
+ fontFamily = fontFamily(context, R.string.wear_material_compose_title_3_font_family)
+ ),
+ body1 =
+ defaultTypography.body1.copy(
+ fontFamily = fontFamily(context, R.string.wear_material_compose_body_1_font_family)
+ ),
+ body2 =
+ defaultTypography.body2.copy(
+ fontFamily = fontFamily(context, R.string.wear_material_compose_body_2_font_family)
+ ),
+ button =
+ defaultTypography.button.copy(
+ fontFamily = fontFamily(context, R.string.wear_material_compose_button_font_family)
+ ),
+ caption1 =
+ defaultTypography.caption1.copy(
+ fontFamily =
+ fontFamily(context, R.string.wear_material_compose_caption_1_font_family)
+ ),
+ caption2 =
+ defaultTypography.caption2.copy(
+ fontFamily =
+ fontFamily(context, R.string.wear_material_compose_caption_2_font_family)
+ ),
+ caption3 =
+ defaultTypography.caption3.copy(
+ fontFamily =
+ fontFamily(context, R.string.wear_material_compose_caption_3_font_family)
+ ),
+ )
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/theme/WearCredentialTonalPalette.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/theme/WearCredentialTonalPalette.kt
new file mode 100644
index 0000000..1d6ed33
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/theme/WearCredentialTonalPalette.kt
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.credentialmanager.ui.theme
+
+import android.R
+import android.content.Context
+import android.os.Build
+import androidx.annotation.ColorRes
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+
+import androidx.compose.ui.graphics.Color
+
+/**
+ * Tonal Palette structure in Material.
+ *
+ * A tonal palette is comprised of 5 tonal ranges. Each tonal range includes the 13 stops, or tonal
+ * swatches.
+ *
+ * Tonal range names are:
+ * - Neutral (N)
+ * - Neutral variant (NV)
+ * - Primary (P)
+ * - Secondary (S)
+ * - Tertiary (T)
+ */
+internal class WearCredentialSelectorTonalPalette(
+ // The neutral tonal range.
+ val neutral100: Color,
+ val neutral99: Color,
+ val neutral95: Color,
+ val neutral90: Color,
+ val neutral80: Color,
+ val neutral70: Color,
+ val neutral60: Color,
+ val neutral50: Color,
+ val neutral40: Color,
+ val neutral30: Color,
+ val neutral20: Color,
+ val neutral10: Color,
+ val neutral0: Color,
+
+ // The neutral variant tonal range, sometimes called "neutral 2"
+ val neutralVariant100: Color,
+ val neutralVariant99: Color,
+ val neutralVariant95: Color,
+ val neutralVariant90: Color,
+ val neutralVariant80: Color,
+ val neutralVariant70: Color,
+ val neutralVariant60: Color,
+ val neutralVariant50: Color,
+ val neutralVariant40: Color,
+ val neutralVariant30: Color,
+ val neutralVariant20: Color,
+ val neutralVariant10: Color,
+ val neutralVariant0: Color,
+
+ // The primary tonal range, also known as accent 1
+ val primary100: Color,
+ val primary99: Color,
+ val primary95: Color,
+ val primary90: Color,
+ val primary80: Color,
+ val primary70: Color,
+ val primary60: Color,
+ val primary50: Color,
+ val primary40: Color,
+ val primary30: Color,
+ val primary20: Color,
+ val primary10: Color,
+ val primary0: Color,
+
+ // The Secondary tonal range, also know as accent 2
+ val secondary100: Color,
+ val secondary99: Color,
+ val secondary95: Color,
+ val secondary90: Color,
+ val secondary80: Color,
+ val secondary70: Color,
+ val secondary60: Color,
+ val secondary50: Color,
+ val secondary40: Color,
+ val secondary30: Color,
+ val secondary20: Color,
+ val secondary10: Color,
+ val secondary0: Color,
+
+ // The tertiary tonal range, also known as accent 3
+ val tertiary100: Color,
+ val tertiary99: Color,
+ val tertiary95: Color,
+ val tertiary90: Color,
+ val tertiary80: Color,
+ val tertiary70: Color,
+ val tertiary60: Color,
+ val tertiary50: Color,
+ val tertiary40: Color,
+ val tertiary30: Color,
+ val tertiary20: Color,
+ val tertiary10: Color,
+ val tertiary0: Color,
+)
+/** Dynamic colors for wear compose material to support resource overlay. */
+@RequiresApi(Build.VERSION_CODES.S)
+// TODO: once we have proper support for this on Wear 6+, we will do something similar to
+// https://source.corp.google.com/h/android/platform/superproject/+/androidx-main:frameworks/support/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DynamicTonalPalette.android.kt;l=307-362?q=dynamicTonalPalette&sq=repo:android%2Fplatform%2Fsuperproject%20b:androidx-main
+// Tracking Bug: b/270720571
+internal fun dynamicTonalPalette(context: Context) =
+ WearCredentialSelectorTonalPalette(
+ // The neutral tonal range from the generated dynamic color palette.
+ neutral100 = ColorResourceHelper.getColor(context, R.color.system_neutral1_0),
+ neutral99 = ColorResourceHelper.getColor(context, R.color.system_neutral1_10),
+ neutral95 = ColorResourceHelper.getColor(context, R.color.system_neutral1_50),
+ neutral90 = ColorResourceHelper.getColor(context, R.color.system_neutral1_100),
+ neutral80 = ColorResourceHelper.getColor(context, R.color.system_neutral1_200),
+ neutral70 = ColorResourceHelper.getColor(context, R.color.system_neutral1_300),
+ neutral60 = ColorResourceHelper.getColor(context, R.color.system_neutral1_400),
+ neutral50 = ColorResourceHelper.getColor(context, R.color.system_neutral1_500),
+ neutral40 = ColorResourceHelper.getColor(context, R.color.system_neutral1_600),
+ neutral30 = ColorResourceHelper.getColor(context, R.color.system_neutral1_700),
+ neutral20 = ColorResourceHelper.getColor(context, R.color.system_neutral1_800),
+ neutral10 = ColorResourceHelper.getColor(context, R.color.system_neutral1_900),
+ neutral0 = ColorResourceHelper.getColor(context, R.color.system_neutral1_1000),
+
+ // The neutral variant tonal range, sometimes called "neutral 2", from the
+ // generated dynamic color palette.
+ neutralVariant100 = ColorResourceHelper.getColor(context, R.color.system_neutral2_0),
+ neutralVariant99 = ColorResourceHelper.getColor(context, R.color.system_neutral2_10),
+ neutralVariant95 = ColorResourceHelper.getColor(context, R.color.system_neutral2_50),
+ neutralVariant90 = ColorResourceHelper.getColor(context, R.color.system_neutral2_100),
+ neutralVariant80 = ColorResourceHelper.getColor(context, R.color.system_neutral2_200),
+ neutralVariant70 = ColorResourceHelper.getColor(context, R.color.system_neutral2_300),
+ neutralVariant60 = ColorResourceHelper.getColor(context, R.color.system_neutral2_400),
+ neutralVariant50 = ColorResourceHelper.getColor(context, R.color.system_neutral2_500),
+ neutralVariant40 = ColorResourceHelper.getColor(context, R.color.system_neutral2_600),
+ neutralVariant30 = ColorResourceHelper.getColor(context, R.color.system_neutral2_700),
+ neutralVariant20 = ColorResourceHelper.getColor(context, R.color.system_neutral2_800),
+ neutralVariant10 = ColorResourceHelper.getColor(context, R.color.system_neutral2_900),
+ neutralVariant0 = ColorResourceHelper.getColor(context, R.color.system_neutral2_1000),
+
+ // The primary tonal range from the generated dynamic color palette.
+ primary100 = ColorResourceHelper.getColor(context, R.color.system_accent1_0),
+ primary99 = ColorResourceHelper.getColor(context, R.color.system_accent1_10),
+ primary95 = ColorResourceHelper.getColor(context, R.color.system_accent1_50),
+ primary90 = ColorResourceHelper.getColor(context, R.color.system_accent1_100),
+ primary80 = ColorResourceHelper.getColor(context, R.color.system_accent1_200),
+ primary70 = ColorResourceHelper.getColor(context, R.color.system_accent1_300),
+ primary60 = ColorResourceHelper.getColor(context, R.color.system_accent1_400),
+ primary50 = ColorResourceHelper.getColor(context, R.color.system_accent1_500),
+ primary40 = ColorResourceHelper.getColor(context, R.color.system_accent1_600),
+ primary30 = ColorResourceHelper.getColor(context, R.color.system_accent1_700),
+ primary20 = ColorResourceHelper.getColor(context, R.color.system_accent1_800),
+ primary10 = ColorResourceHelper.getColor(context, R.color.system_accent1_900),
+ primary0 = ColorResourceHelper.getColor(context, R.color.system_accent1_1000),
+
+ // The secondary tonal range from the generated dynamic color palette.
+ secondary100 = ColorResourceHelper.getColor(context, R.color.system_accent2_0),
+ secondary99 = ColorResourceHelper.getColor(context, R.color.system_accent2_10),
+ secondary95 = ColorResourceHelper.getColor(context, R.color.system_accent2_50),
+ secondary90 = ColorResourceHelper.getColor(context, R.color.system_accent2_100),
+ secondary80 = ColorResourceHelper.getColor(context, R.color.system_accent2_200),
+ secondary70 = ColorResourceHelper.getColor(context, R.color.system_accent2_300),
+ secondary60 = ColorResourceHelper.getColor(context, R.color.system_accent2_400),
+ secondary50 = ColorResourceHelper.getColor(context, R.color.system_accent2_500),
+ secondary40 = ColorResourceHelper.getColor(context, R.color.system_accent2_600),
+ secondary30 = ColorResourceHelper.getColor(context, R.color.system_accent2_700),
+ secondary20 = ColorResourceHelper.getColor(context, R.color.system_accent2_800),
+ secondary10 = ColorResourceHelper.getColor(context, R.color.system_accent2_900),
+ secondary0 = ColorResourceHelper.getColor(context, R.color.system_accent2_1000),
+
+ // The tertiary tonal range from the generated dynamic color palette.
+ tertiary100 = ColorResourceHelper.getColor(context, R.color.system_accent3_0),
+ tertiary99 = ColorResourceHelper.getColor(context, R.color.system_accent3_10),
+ tertiary95 = ColorResourceHelper.getColor(context, R.color.system_accent3_50),
+ tertiary90 = ColorResourceHelper.getColor(context, R.color.system_accent3_100),
+ tertiary80 = ColorResourceHelper.getColor(context, R.color.system_accent3_200),
+ tertiary70 = ColorResourceHelper.getColor(context, R.color.system_accent3_300),
+ tertiary60 = ColorResourceHelper.getColor(context, R.color.system_accent3_400),
+ tertiary50 = ColorResourceHelper.getColor(context, R.color.system_accent3_500),
+ tertiary40 = ColorResourceHelper.getColor(context, R.color.system_accent3_600),
+ tertiary30 = ColorResourceHelper.getColor(context, R.color.system_accent3_700),
+ tertiary20 = ColorResourceHelper.getColor(context, R.color.system_accent3_800),
+ tertiary10 = ColorResourceHelper.getColor(context, R.color.system_accent3_900),
+ tertiary0 = ColorResourceHelper.getColor(context, R.color.system_accent3_1000),
+ )
+
+private object ColorResourceHelper {
+ @DoNotInline
+ fun getColor(context: Context, @ColorRes id: Int): Color {
+ return Color(context.resources.getColor(id, context.theme))
+ }
+}
\ No newline at end of file
diff --git a/packages/InputDevices/res/raw/keyboard_layout_thai_kedmanee.kcm b/packages/InputDevices/res/raw/keyboard_layout_thai_kedmanee.kcm
new file mode 100644
index 0000000..2283032
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_thai_kedmanee.kcm
@@ -0,0 +1,321 @@
+# Copyright 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Thai Kedmanee keyboard layout.
+#
+
+type OVERLAY
+
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+ label: '_'
+ base: '_'
+ shift, capslock: '%'
+}
+
+key 1 {
+ label: '\u0e45'
+ base: '\u0e45'
+ shift, capslock: '+'
+}
+
+key 2 {
+ label: '/'
+ base: '/'
+ shift, capslock: '\u0e51'
+}
+
+key 3 {
+ label: '-'
+ base: '-'
+ shift, capslock: '\u0e52'
+}
+
+key 4 {
+ label: '\u0e20'
+ base: '\u0e20'
+ shift, capslock: '\u0e53'
+}
+
+key 5 {
+ label: '\u0e16'
+ base: '\u0e16'
+ shift, capslock: '\u0e54'
+}
+
+key 6 {
+ label: '\u0e38'
+ base: '\u0e38'
+ shift, capslock: '\u0e39'
+}
+
+key 7 {
+ label: '\u0e36'
+ base: '\u0e36'
+ shift, capslock: '\u0e3f'
+}
+
+key 8 {
+ label: '\u0e04'
+ base: '\u0e04'
+ shift, capslock: '\u0e55'
+}
+
+key 9 {
+ label: '\u0e15'
+ base: '\u0e15'
+ shift, capslock: '\u0e56'
+}
+
+key 0 {
+ label: '\u0e08'
+ base: '\u0e08'
+ shift, capslock: '\u0e57'
+}
+
+key MINUS {
+ label: '\u0e02'
+ base: '\u0e02'
+ shift, capslock: '\u0e58'
+}
+
+key EQUALS {
+ label: '\u0e0a'
+ base: '\u0e0a'
+ shift, capslock: '\u0e59'
+}
+
+### ROW 2
+
+key Q {
+ label: '\u0e46'
+ base: '\u0e46'
+ shift, capslock: '\u0e50'
+}
+
+key W {
+ label: '\u0e44'
+ base: '\u0e44'
+ shift, capslock: '\u0022'
+}
+
+key E {
+ label: '\u0e33'
+ base: '\u0e33'
+ shift, capslock: '\u0e0e'
+}
+
+key R {
+ label: '\u0e1e'
+ base: '\u0e1e'
+ shift, capslock: '\u0e11'
+}
+
+key T {
+ label: '\u0e30'
+ base: '\u0e30'
+ shift, capslock: '\u0e18'
+}
+
+key Y {
+ label: '\u0e31'
+ base: '\u0e31'
+ shift, capslock: '\u0e4d'
+}
+
+key U {
+ label: '\u0e35'
+ base: '\u0e35'
+ shift, capslock: '\u0e4a'
+}
+
+key I {
+ label: '\u0e23'
+ base: '\u0e23'
+ shift, capslock: '\u0e13'
+}
+
+key O {
+ label: '\u0e19'
+ base: '\u0e19'
+ shift, capslock: '\u0e2f'
+}
+
+key P {
+ label: '\u0e22'
+ base: '\u0e22'
+ shift, capslock: '\u0e0d'
+}
+
+key LEFT_BRACKET {
+ label: '\u0e1a'
+ base: '\u0e1a'
+ shift, capslock: '\u0e10'
+ ctrl: '%'
+}
+
+key RIGHT_BRACKET {
+ label: '\u0e25'
+ base: '\u0e25'
+ shift, capslock: ','
+ ctrl: '\u0e51'
+}
+
+### ROW 3
+
+key A {
+ label: '\u0e1f'
+ base: '\u0e1f'
+ shift, capslock: '\u0e24'
+}
+
+key S {
+ label: '\u0e2b'
+ base: '\u0e2b'
+ shift, capslock: '\u0e06'
+}
+
+key D {
+ label: '\u0e01'
+ base: '\u0e01'
+ shift, capslock: '\u0e0f'
+}
+
+key F {
+ label: '\u0e14'
+ base: '\u0e14'
+ shift, capslock: '\u0e42'
+}
+
+key G {
+ label: '\u0e40'
+ base: '\u0e40'
+ shift, capslock: '\u0e0c'
+}
+
+key H {
+ label: '\u0e49'
+ base: '\u0e49'
+ shift, capslock: '\u0e47'
+}
+
+key J {
+ label: '\u0e48'
+ base: '\u0e48'
+ shift, capslock: '\u0e4b'
+}
+
+key K {
+ label: '\u0e32'
+ base: '\u0e32'
+ shift, capslock: '\u0e29'
+}
+
+key L {
+ label: '\u0e2a'
+ base: '\u0e2a'
+ shift, capslock: '\u0e28'
+}
+
+key SEMICOLON {
+ label: '\u0e27'
+ base: '\u0e27'
+ shift, capslock: '\u0e0b'
+}
+
+key APOSTROPHE {
+ label: '\u0e07'
+ base: '\u0e07'
+ shift, capslock: '.'
+}
+
+key BACKSLASH {
+ label: '\u0e03'
+ base: '\u0e03'
+ shift, capslock: '\u0e05'
+ ctrl: '+'
+}
+
+### ROW 4
+
+key PLUS {
+ label: '\u0e03'
+ base: '\u0e03'
+ shift, capslock: '\u0e05'
+ ctrl: '\u0e52'
+}
+
+key Z {
+ label: '\u0e1c'
+ base: '\u0e1c'
+ shift, capslock: '('
+}
+
+key X {
+ label: '\u0e1b'
+ base: '\u0e1b'
+ shift, capslock: ')'
+}
+
+key C {
+ label: '\u0e41'
+ base: '\u0e41'
+ shift, capslock: '\u0e09'
+}
+
+key V {
+ label: '\u0e2d'
+ base: '\u0e2d'
+ shift, capslock: '\u0e2e'
+}
+
+key B {
+ label: '\u0e34'
+ base: '\u0e34'
+ shift, capslock: '\u0e3a'
+}
+
+key N {
+ label: '\u0e37'
+ base: '\u0e37'
+ shift, capslock: '\u0e4c'
+}
+
+key M {
+ label: '\u0e17'
+ base: '\u0e17'
+ shift, capslock: '?'
+}
+
+key COMMA {
+ label: '\u0e21'
+ base: '\u0e21'
+ shift, capslock: '\u0e12'
+}
+
+key PERIOD {
+ label: '\u0e43'
+ base: '\u0e43'
+ shift, capslock: '\u0e2c'
+}
+
+key SLASH {
+ label: '\u0e1d'
+ base: '\u0e1d'
+ shift, capslock: '\u0e26'
+}
\ No newline at end of file
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index 7208894..5d0c022 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarussies"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongools"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgies"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index 1698150..39d5717 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ቤላሩስኛ"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ሞንጎሊያኛ"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ጂዮርጂያኛ"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index 8ed1972..4315853 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"البيلاروسية"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"المنغولية"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"الجورجية"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-as/strings.xml b/packages/InputDevices/res/values-as/strings.xml
index 9744a7d..0171442 100644
--- a/packages/InputDevices/res/values-as/strings.xml
+++ b/packages/InputDevices/res/values-as/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"বেলাৰুছিয়ান"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml
index ee3a337..39a12b7 100644
--- a/packages/InputDevices/res/values-az/strings.xml
+++ b/packages/InputDevices/res/values-az/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarus dili"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Monqol"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gürcü"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
index 1fc84f3..64aa7f6 100644
--- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml
+++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"beloruski"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolska"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzijska"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml
index 50c5910..a8c11be 100644
--- a/packages/InputDevices/res/values-be/strings.xml
+++ b/packages/InputDevices/res/values-be/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Беларуская"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Мангольская"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузінская"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index 1ee9565..6d82a0b 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"беларуски"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"монголски"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузински"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml
index a996538..3fc1315 100644
--- a/packages/InputDevices/res/values-bn/strings.xml
+++ b/packages/InputDevices/res/values-bn/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"বেলারুশীয়"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"মঙ্গোলিয়ান"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"জর্জিয়ান"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml
index c6cacbc..b2cf525 100644
--- a/packages/InputDevices/res/values-bs/strings.xml
+++ b/packages/InputDevices/res/values-bs/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"bjeloruska"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolski"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzijski"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index 761c248..ed04b94 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorús"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgià"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index 3f1b3d0..5cd3ff5 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"běloruština"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolština"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzínština"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index b160341..4a6c70c 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Hviderussisk"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolsk"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisk"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index 17d6e4a..628a742 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarussisch"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolisch"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisch"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index 1b9b42e..7b9651c 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Λευκορωσικά"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Μογγολικά"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Γεωργιανά"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml
index ab48729..76bb2f1 100644
--- a/packages/InputDevices/res/values-en-rAU/strings.xml
+++ b/packages/InputDevices/res/values-en-rAU/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml
index 1161783..ac8ad0a 100644
--- a/packages/InputDevices/res/values-en-rCA/strings.xml
+++ b/packages/InputDevices/res/values-en-rCA/strings.xml
@@ -50,4 +50,5 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
+ <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index ab48729..76bb2f1 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index ab48729..76bb2f1 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml
index 92c5a5c..159b0e0 100644
--- a/packages/InputDevices/res/values-en-rXC/strings.xml
+++ b/packages/InputDevices/res/values-en-rXC/strings.xml
@@ -50,4 +50,5 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
+ <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
</resources>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index b9fe046..05f5473 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -4,7 +4,7 @@
<string name="app_label" msgid="8016145283189546017">"Dispositivos de entrada"</string>
<string name="keyboard_layouts_label" msgid="6688773268302087545">"Teclado de Android"</string>
<string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"Inglés (Reino Unido)"</string>
- <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"Inglés (EE. UU.)"</string>
+ <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"Inglés (EE.UU.)"</string>
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Inglés (EE. UU.), internacional"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Inglés (EE. UU.), Colemak"</string>
<string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Inglés (EE. UU.), Dvorak"</string>
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorruso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index 216e79f..cd68ad8 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorruso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml
index 87c486f..9b37264 100644
--- a/packages/InputDevices/res/values-et/strings.xml
+++ b/packages/InputDevices/res/values-et/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"valgevene"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongoli"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruusia"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index 64628a82..61c6415 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorrusiarra"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongoliarra"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiarra"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index a086060..4389205 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"بلاروسی"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"مغولی"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"گرجستانی"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index f3bef4d..0e8efff 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"valkovenäjä"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongoli"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"georgia"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 63635824..e176c7e 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Biélorusse"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Géorgien"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index 37cde05..4388ec1 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Biélorusse"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Géorgien"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml
index 9995f79..827071e 100644
--- a/packages/InputDevices/res/values-gl/strings.xml
+++ b/packages/InputDevices/res/values-gl/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belaruso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Xeorxiano"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml
index 9dfda76..df095ae 100644
--- a/packages/InputDevices/res/values-gu/strings.xml
+++ b/packages/InputDevices/res/values-gu/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"બેલારુશિયન"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"મોંગોલિયન"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"જ્યોર્જિઅન"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 2562854..550759d 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"बेलारूसी"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"मंगोलियन"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"जॉर्जियन कीबोर्ड का लेआउट"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index 6832437..e955e77 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"bjeloruski"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolski"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzijska"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index f2ac8a2..565bf69 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"belarusz"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongol"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"grúz"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml
index a6676fe..9722342 100644
--- a/packages/InputDevices/res/values-hy/strings.xml
+++ b/packages/InputDevices/res/values-hy/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"բելառուսերեն"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Մոնղոլերեն"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"վրացերեն"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index 92bd24d..19fd692 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusia"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolia"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgia"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml
index b761a7e..f76d9d7 100644
--- a/packages/InputDevices/res/values-is/strings.xml
+++ b/packages/InputDevices/res/values-is/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"hvítrússneska"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongólska"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"georgíska"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index a992b86..ccde851 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorusso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolo"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index de9b276..4a8b802 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"בלארוסית"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"מונגולית"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"גיאורגית"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index 6be0b4c..b9bab69 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ベラルーシ語"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"モンゴル語"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ジョージア語"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml
index 92d9470..1610d26 100644
--- a/packages/InputDevices/res/values-ka/strings.xml
+++ b/packages/InputDevices/res/values-ka/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ბელორუსული"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"მონღოლური"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ქართული"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml
index c8ab796..43bea18 100644
--- a/packages/InputDevices/res/values-kk/strings.xml
+++ b/packages/InputDevices/res/values-kk/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Белорус"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Моңғол"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузин"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index fd63ab33c..eb43247 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"បេឡារុស"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"មុងហ្គោលី"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ហ្សកហ្ស៊ី"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml
index 761b7cc..5750585 100644
--- a/packages/InputDevices/res/values-kn/strings.xml
+++ b/packages/InputDevices/res/values-kn/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ಬೆಲರೂಸಿಯನ್"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ಮಂಗೋಲಿಯನ್"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ಜಾರ್ಜಿಯನ್"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index 2a1cbb0..b442492 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"벨라루스어"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"몽골어"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"조지아어"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml
index 70295e1..36cd539 100644
--- a/packages/InputDevices/res/values-ky/strings.xml
+++ b/packages/InputDevices/res/values-ky/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Беларусча"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Монголчо"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузинче"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml
index 2b8946b..ab64e24 100644
--- a/packages/InputDevices/res/values-lo/strings.xml
+++ b/packages/InputDevices/res/values-lo/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ເບລາຣັສຊຽນ"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ມອງໂກລຽນ"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ຈໍຈຽນ"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index 5fca46b..ec98937 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Baltarusių k."</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolų"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzinų"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index d225329..05e5d2f 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Baltkrievu"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongoļu"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzīnu"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index d98b58b..c2bc8c0 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"белоруски"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"монголски"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузиски"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml
index f346881..33f8f22 100644
--- a/packages/InputDevices/res/values-ml/strings.xml
+++ b/packages/InputDevices/res/values-ml/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ബെലാറുഷ്യൻ"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"മംഗോളിയൻ"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ജോര്ജ്ജിയൻ"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml
index 1697097..7aad90d 100644
--- a/packages/InputDevices/res/values-mn/strings.xml
+++ b/packages/InputDevices/res/values-mn/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Беларусь хэл"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Монгол"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Гүрж"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml
index 6382f6f..db607e3 100644
--- a/packages/InputDevices/res/values-mr/strings.xml
+++ b/packages/InputDevices/res/values-mr/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"बेलारुशियन"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"मंगोलियन"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"जॉर्जियन"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml
index 9bff171..9a00126 100644
--- a/packages/InputDevices/res/values-ms/strings.xml
+++ b/packages/InputDevices/res/values-ms/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bahasa Belarus"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Bahasa Mongolia"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Bahasa Georgia"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml
index 01b5507..d87a7b4 100644
--- a/packages/InputDevices/res/values-my/strings.xml
+++ b/packages/InputDevices/res/values-my/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ဘီလာရုဇ်"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"မွန်ဂိုလီးယား"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ဂျော်ဂျီယာ"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index 40eb25a..fbefd38 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusisk"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolsk"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisk"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml
index 6e98bf6..642fc5c 100644
--- a/packages/InputDevices/res/values-ne/strings.xml
+++ b/packages/InputDevices/res/values-ne/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"बेलारुसियाली"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"मङ्गोलियाली"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"जर्जियाली"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index f3a5814..a93772f 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Wit-Russisch"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongools"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisch"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-or/strings.xml b/packages/InputDevices/res/values-or/strings.xml
index 5b6aaea..47af22e 100644
--- a/packages/InputDevices/res/values-or/strings.xml
+++ b/packages/InputDevices/res/values-or/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ବେଲାରୁସିଆନ୍"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ମଙ୍ଗୋଲିଆନ୍"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ଜର୍ଜିଆନ୍"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml
index 988b449..c634223 100644
--- a/packages/InputDevices/res/values-pa/strings.xml
+++ b/packages/InputDevices/res/values-pa/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ਬੇਲਾਰੂਸੀ"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ਮੰਗੋਲੀਆਈ"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ਜਾਰਜੀਆਈ"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index 4d18215..31a3dac 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -50,4 +50,5 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"białoruski"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolski"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruziński"</string>
+ <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajski (Kedmanee)"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml
index e44f4ae..aba7afc4 100644
--- a/packages/InputDevices/res/values-pt-rBR/strings.xml
+++ b/packages/InputDevices/res/values-pt-rBR/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorrusso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index 3ad3e63..3d7c603 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorrusso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index e44f4ae..aba7afc4 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorrusso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index 3867e1c..4cdd54a 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusă"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolă"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiană"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 7c5c95a..fcdd405 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"белорусский"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"монгольский"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузинский"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml
index f4147f3..44dfd60 100644
--- a/packages/InputDevices/res/values-si/strings.xml
+++ b/packages/InputDevices/res/values-si/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"බෙලරුසියානු"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"මොන්ගෝලියානු"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ජෝර්ජියානු"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index 2a37552..f02b0be 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"bieloruské"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolské"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzínske"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index 334326c..ab70e08 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"beloruščina"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolščina"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzinščina"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index 0863138..d01b2bc 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bjellorusisht"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolisht"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gjeorgjisht"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index 973b833..88978f6 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"белоруски"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"монголска"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузијска"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index b1e5d75..a255d24 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"vitryska"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongoliska"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"georgiska"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index 0f4c846..00979e5 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Kibelarusi"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Kimongolia"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Kijojia"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml
index d3535d4..355f78d 100644
--- a/packages/InputDevices/res/values-ta/strings.xml
+++ b/packages/InputDevices/res/values-ta/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"பெலரூசியன்"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"மங்கோலியன்"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ஜார்ஜியன்"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml
index 5a10f6e..1fe885d 100644
--- a/packages/InputDevices/res/values-te/strings.xml
+++ b/packages/InputDevices/res/values-te/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"బెలారష్యన్"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"మంగోలియన్"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"జార్జియన్"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index 131c536..f1b433b 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"เบลารุส"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ภาษามองโกเลีย"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"ภาษาจอร์เจีย"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index c42adbc..21ad909 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index 1a84d0ac..a1ac7fd 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusça"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Moğolca"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gürcüce"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index 71b3496..dcc5bcf 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Білоруська"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Монгольська"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузинська"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml
index 0cc9b61..e8f327c 100644
--- a/packages/InputDevices/res/values-ur/strings.xml
+++ b/packages/InputDevices/res/values-ur/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"بيلاروسی"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"منگؤلی"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"جارجیائی"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml
index 52ecdfc..0968d7a 100644
--- a/packages/InputDevices/res/values-uz/strings.xml
+++ b/packages/InputDevices/res/values-uz/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarus"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzin"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index 0c638fa..f7c3658 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Tiếng Belarus"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Tiếng Mông Cổ"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Tiếng Georgia"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index b249779..7d0a128 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"白俄罗斯语"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"蒙古语"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"格鲁吉亚语"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index 60a52e9..a0c3c1a 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"白俄羅斯文"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"蒙古文"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"格魯吉亞文"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index c3217e3..1b84841 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"白俄羅斯文"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"蒙古文"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"喬治亞文"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index 88d55f2..c4b5d7a 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -50,4 +50,6 @@
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"isi-Mongolian"</string>
<string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
+ <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) -->
+ <skip />
</resources>
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 1e13940..33a1d76 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -146,4 +146,7 @@
<!-- Georgian keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_georgian">Georgian</string>
+
+ <!-- Thai (Kedmanee variant) keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_thai_kedmanee">Thai (Kedmanee)</string>
</resources>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index ee49b23..4b7ea90 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -318,4 +318,11 @@
android:label="@string/keyboard_layout_georgian"
android:keyboardLayout="@raw/keyboard_layout_georgian"
android:keyboardLocale="ka-Geor" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_thai_kedmanee"
+ android:label="@string/keyboard_layout_thai_kedmanee"
+ android:keyboardLayout="@raw/keyboard_layout_thai_kedmanee"
+ android:keyboardLocale="th-Thai"
+ android:keyboardLayoutType="extended" />
</keyboard-layouts>
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
index 140fa36..13661e3 100644
--- a/packages/PackageInstaller/res/values-af/strings.xml
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -58,7 +58,7 @@
<string name="uninstall_application_title" msgid="4045420072401428123">"Deïnstalleer program"</string>
<string name="uninstall_update_title" msgid="824411791011583031">"Deïnstalleer opdatering"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> is deel van die volgende program:"</string>
- <string name="uninstall_application_text" msgid="3816830743706143980">"Wil jy hierdie program deïnstalleer?"</string>
+ <string name="uninstall_application_text" msgid="3816830743706143980">"Wil jy hierdie app deïnstalleer?"</string>
<string name="archive_application_text" msgid="8482325710714386348">"Jou persoonlike data sal gestoor word"</string>
<string name="archive_application_text_all_users" msgid="3151229641681672580">"Argiveer hierdie app vir alle gebruikers? Jou persoonlike data sal gestoor word"</string>
<string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Argiveer hierdie app op jou werkprofiel? Jou persoonlike data sal gestoor word"</string>
diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml
index f642e145..82d5414 100644
--- a/packages/PackageInstaller/res/values-gu/strings.xml
+++ b/packages/PackageInstaller/res/values-gu/strings.xml
@@ -58,7 +58,7 @@
<string name="uninstall_application_title" msgid="4045420072401428123">"ઍપ્લિકેશન અનઇન્સ્ટૉલ કરો"</string>
<string name="uninstall_update_title" msgid="824411791011583031">"અપડેટ અનઇન્સ્ટૉલ કરો"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, નીચેની ઍપ્લિકેશનનો ભાગ છે:"</string>
- <string name="uninstall_application_text" msgid="3816830743706143980">"શું તમે આ ઍપને અનઇન્સ્ટૉલ કરવા માંગો છો?"</string>
+ <string name="uninstall_application_text" msgid="3816830743706143980">"શું તમે આ ઍપને અનઇન્સ્ટૉલ કરવા માગો છો?"</string>
<string name="archive_application_text" msgid="8482325710714386348">"તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string>
<string name="archive_application_text_all_users" msgid="3151229641681672580">"શું આ ઍપને તમામ વપરાશકર્તાઓ માટે આર્કાઇવ કરીએ? તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string>
<string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"શું આ ઍપને તમારી ઑફિસની પ્રોફાઇલ પર આર્કાઇવ કરીએ? તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string>
diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml
index f2bc41e..d9fb570 100644
--- a/packages/PackageInstaller/res/values-hy/strings.xml
+++ b/packages/PackageInstaller/res/values-hy/strings.xml
@@ -58,7 +58,7 @@
<string name="uninstall_application_title" msgid="4045420072401428123">"Հավելվածի ապատեղադրում"</string>
<string name="uninstall_update_title" msgid="824411791011583031">"Ապատեղադրել թարմացումը"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> գործողությունը հետևյալ հավելվածի մասն է`"</string>
- <string name="uninstall_application_text" msgid="3816830743706143980">"Ուզո՞ւմ եք ապատեղադրել այս հավելվածը։"</string>
+ <string name="uninstall_application_text" msgid="3816830743706143980">"Ուզում եք ապատեղադրե՞լ այս հավելվածը։"</string>
<string name="archive_application_text" msgid="8482325710714386348">"Ձեր անձնական տվյալները կպահվեն"</string>
<string name="archive_application_text_all_users" msgid="3151229641681672580">"Արխիվացնե՞լ այս հավելվածը բոլոր օգտատերերի համար։ Ձեր անձնական տվյալները կպահվեն"</string>
<string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Արխիվացնե՞լ այս հավելվածը ձեր աշխատանքային պրոֆիլում։ Ձեր անձնական տվյալները կպահվեն"</string>
diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml
index e788bf7..55f960d 100644
--- a/packages/PackageInstaller/res/values-ky/strings.xml
+++ b/packages/PackageInstaller/res/values-ky/strings.xml
@@ -63,7 +63,7 @@
<string name="archive_application_text_all_users" msgid="3151229641681672580">"Бул колдонмо бардык колдонуучулар үчүн архивделсинби? Жеке маалыматыңыз сакталат"</string>
<string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Бул колдонмо жумуш профилиңизде архивделсинби? Жеке маалыматыңыз сакталат"</string>
<string name="archive_application_text_user" msgid="2586558895535581451">"Бул колдонмо <xliff:g id="USERNAME">%1$s</xliff:g> үчүн архивделсинби? Жеке маалыматыңыз сакталат"</string>
- <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Бул колдонмо жеке чөйрөдөн архивделсинби? Жеке маалыматыңыз сакталат"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Бул колдонмо жеке мейкиндиктен архивделсинби? Жеке маалыматыңыз сакталат"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Бул колдонмо "<b>"бардык"</b>" колдонуучулардан алынып салынсынбы? Бул колдонмо жана анын дайындары бул түзмөктүн "<b>"бардык"</b>" колдонуучуларынан өчүрүлөт."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Бул колдонмону <xliff:g id="USERNAME">%1$s</xliff:g> үчүн чыгарып саласызбы?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Бул колдонмону жумуш профилиңизден чыгарып саласызбы?"</string>
@@ -72,7 +72,7 @@
<string name="uninstall_keep_data" msgid="7002379587465487550">"Колдонмонун <xliff:g id="SIZE">%1$s</xliff:g> дайындарын сактоо."</string>
<string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Бул колдонмону өчүрөсүзбү?"</string>
<string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Бул колдонмону чыгарып саласызбы? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> клону да өчүрүлөт."</string>
- <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Бул колдонмону жеке чөйрөдөн чыгарып саласызбы?"</string>
+ <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Бул колдонмону жеке мейкиндиктен чыгарып саласызбы?"</string>
<string name="uninstalling_notification_channel" msgid="840153394325714653">"Чыгарылып салынууда"</string>
<string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Чыгарылып салынбай калгандар"</string>
<string name="uninstalling" msgid="8709566347688966845">"Чыгарылып салынууда…"</string>
diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml
index 149783d..bd288e4 100644
--- a/packages/PackageInstaller/res/values-nl/strings.xml
+++ b/packages/PackageInstaller/res/values-nl/strings.xml
@@ -98,7 +98,7 @@
<string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"Uit veiligheidsoverwegingen heeft je tv momenteel geen toestemming om onbekende apps van deze bron te installeren. Je kunt dit wijzigen via Instellingen."</string>
<string name="untrusted_external_source_warning" product="watch" msgid="7195163388090818636">"Uit veiligheidsoverwegingen heeft je smartwatch momenteel geen toestemming om onbekende apps van deze bron te installeren. Je kunt dit wijzigen via Instellingen."</string>
<string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"Uit veiligheidsoverwegingen heeft je telefoon momenteel geen toestemming om onbekende apps van deze bron te installeren. Je kunt dit wijzigen via Instellingen."</string>
- <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Je telefoon en persoonsgegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je telefoon of gegevensverlies als gevolg van het gebruik van de app."</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Je telefoon en persoonsgegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Door deze app te installeren, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je telefoon of gegevensverlies als gevolg van het gebruik van de app."</string>
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Je tablet en persoonsgegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je tablet of gegevensverlies als gevolg van het gebruik van de app."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Je tv en persoonsgegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je tv of gegevensverlies als gevolg van het gebruik van de app."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-kloon"</string>
diff --git a/packages/PackageInstaller/res/values-th/strings.xml b/packages/PackageInstaller/res/values-th/strings.xml
index 7f37084..739d272 100644
--- a/packages/PackageInstaller/res/values-th/strings.xml
+++ b/packages/PackageInstaller/res/values-th/strings.xml
@@ -94,10 +94,10 @@
<string name="Parse_error_dlg_text" msgid="1661404001063076789">"พบปัญหาในการแยกวิเคราะห์แพ็กเกจ"</string>
<string name="message_staging" msgid="8032722385658438567">"กำลังปรับสภาพแวดล้อมของแอป…"</string>
<string name="app_name_unknown" msgid="6881210203354323926">"ไม่ทราบ"</string>
- <string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"เพื่อความปลอดภัย ปัจจุบันไม่อนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ในแท็บเล็ต คุณเปลี่ยนแปลงได้ในการตั้งค่า"</string>
- <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"เพื่อความปลอดภัย ปัจจุบันไม่อนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ในทีวี คุณเปลี่ยนแปลงได้ในการตั้งค่า"</string>
- <string name="untrusted_external_source_warning" product="watch" msgid="7195163388090818636">"ปัจจุบันนาฬิกาไม่อนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้เพื่อความปลอดภัยของคุณ คุณเปลี่ยนค่านี้ได้ในการตั้งค่า"</string>
- <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"เพื่อความปลอดภัย ปัจจุบันไม่อนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ในโทรศัพท์ ซึ่งคุณเปลี่ยนเป็นอนุญาตได้ในการตั้งค่า"</string>
+ <string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"เพื่อความปลอดภัย โทรศัพท์ของคุณไม่ได้รับอนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ คุณสามารถเปลี่ยนแปลงได้ที่ \"การตั้งค่า\""</string>
+ <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"เพื่อความปลอดภัย โทรศัพท์ของคุณไม่ได้รับอนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ คุณสามารถเปลี่ยนแปลงได้ที่ \"การตั้งค่า\""</string>
+ <string name="untrusted_external_source_warning" product="watch" msgid="7195163388090818636">"เพื่อความปลอดภัย นาฬิกาของคุณไม่ได้รับอนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ คุณสามารถเปลี่ยนแปลงได้ที่ \"การตั้งค่า\""</string>
+ <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"เพื่อความปลอดภัย โทรศัพท์ของคุณไม่ได้รับอนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ คุณสามารถเปลี่ยนแปลงได้ที่ \"การตั้งค่า\""</string>
<string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"โทรศัพท์และข้อมูลส่วนตัวของคุณมีความเสี่ยงมากขึ้นที่จะถูกโจมตีจากแอปที่ไม่รู้จัก การติดตั้งแอปนี้แสดงว่าคุณยอมรับว่าจะรับผิดชอบต่อความเสียหายใดๆ ที่จะเกิดขึ้นกับโทรศัพท์หรือการสูญเสียข้อมูลที่อาจเกิดจากการใช้งานแอปดังกล่าว"</string>
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"แท็บเล็ตและข้อมูลส่วนตัวของคุณมีความเสี่ยงมากขึ้นที่จะถูกโจมตีจากแอปที่ไม่รู้จัก การติดตั้งแอปนี้แสดงว่าคุณยอมรับว่าจะรับผิดชอบต่อความเสียหายใดๆ ที่จะเกิดขึ้นกับแท็บเล็ตหรือการสูญเสียข้อมูลที่อาจเกิดจากการใช้งานแอปดังกล่าว"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ทีวีและข้อมูลส่วนตัวของคุณมีความเสี่ยงมากขึ้นที่จะถูกโจมตีจากแอปที่ไม่รู้จัก การติดตั้งแอปนี้แสดงว่าคุณยอมรับว่าจะรับผิดชอบต่อความเสียหายใดๆ ที่จะเกิดขึ้นกับทีวีหรือการสูญเสียข้อมูลที่อาจเกิดจากการใช้งานแอปดังกล่าว"</string>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index 5298f8b..6ffad70 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -35,7 +35,7 @@
<string name="install_for_print_preview" msgid="6366303997385509332">"Installer un lecteur PDF pour voir l\'aperçu"</string>
<string name="printing_app_crashed" msgid="854477616686566398">"L\'application à l\'origine de l\'impression a planté"</string>
<string name="generating_print_job" msgid="3119608742651698916">"Génération tâche impression…"</string>
- <string name="save_as_pdf" msgid="5718454119847596853">"Enregistrer en format PDF"</string>
+ <string name="save_as_pdf" msgid="5718454119847596853">"Enregistrer au format PDF"</string>
<string name="all_printers" msgid="5018829726861876202">"Toutes les imprimantes…"</string>
<string name="print_dialog" msgid="32628687461331979">"Boîte de dialogue d\'impression"</string>
<string name="current_page_template" msgid="5145005201131935302">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> sur <xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-night/themes.xml b/packages/PrintSpooler/res/values-night/themes.xml
index 4428dbb..3cc64a6 100644
--- a/packages/PrintSpooler/res/values-night/themes.xml
+++ b/packages/PrintSpooler/res/values-night/themes.xml
@@ -30,6 +30,7 @@
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
</resources>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index 4dcad10..bd96025 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -31,6 +31,7 @@
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowLightStatusBar">true</item>
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
</resources>
diff --git a/packages/SettingsLib/DataStore/Android.bp b/packages/SettingsLib/DataStore/Android.bp
index 9fafcab..86c8f0da 100644
--- a/packages/SettingsLib/DataStore/Android.bp
+++ b/packages/SettingsLib/DataStore/Android.bp
@@ -2,12 +2,17 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+filegroup {
+ name: "SettingsLibDataStore-srcs",
+ srcs: ["src/**/*"],
+}
+
android_library {
name: "SettingsLibDataStore",
defaults: [
"SettingsLintDefaults",
],
- srcs: ["src/**/*"],
+ srcs: [":SettingsLibDataStore-srcs"],
static_libs: [
"androidx.annotation_annotation",
"androidx.collection_collection-ktx",
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreFileArchiver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreFileArchiver.kt
index 9d3fb66..7644bc9 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreFileArchiver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreFileArchiver.kt
@@ -19,6 +19,7 @@
import android.app.backup.BackupDataInputStream
import android.content.Context
import android.util.Log
+import androidx.annotation.VisibleForTesting
import java.io.File
import java.io.InputStream
import java.io.OutputStream
@@ -33,11 +34,9 @@
*/
internal class BackupRestoreFileArchiver(
private val context: Context,
- private val fileStorages: List<BackupRestoreFileStorage>,
+ @get:VisibleForTesting internal val fileStorages: List<BackupRestoreFileStorage>,
+ override val name: String,
) : BackupRestoreStorage() {
- override val name: String
- get() = "file_archiver"
-
override fun createBackupRestoreEntities(): List<BackupRestoreEntity> =
fileStorages.map { it.toBackupRestoreEntity() }
@@ -88,7 +87,8 @@
}
}
-private fun BackupRestoreFileStorage.toBackupRestoreEntity() =
+@VisibleForTesting
+internal fun BackupRestoreFileStorage.toBackupRestoreEntity() =
object : BackupRestoreEntity {
override val key: String
get() = storageFilePath
@@ -107,7 +107,7 @@
Log.i(LOG_TAG, "[$name] $key not exist")
return EntityBackupResult.DELETE
}
- val codec = codec() ?: defaultCodec()
+ val codec = defaultCodec()
// MUST close to flush the data
wrapBackupOutputStream(codec, outputStream).use { stream ->
val bytesCopied = file.inputStream().use { it.copyTo(stream) }
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
index c4c00cb..935f9cc 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
@@ -22,6 +22,7 @@
import android.app.backup.BackupHelper
import android.os.ParcelFileDescriptor
import android.util.Log
+import androidx.annotation.VisibleForTesting
import androidx.collection.MutableScatterMap
import com.google.common.io.ByteStreams
import java.io.ByteArrayOutputStream
@@ -60,10 +61,11 @@
*
* Map key is the entity key, map value is the checksum of backup data.
*/
- protected val entityStates = MutableScatterMap<String, Long>()
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ val entityStates = MutableScatterMap<String, Long>()
/** Entities created by [createBackupRestoreEntities]. This field is for restore only. */
- private var entities: List<BackupRestoreEntity>? = null
+ @VisibleForTesting internal var entities: List<BackupRestoreEntity>? = null
/** Entities to back up and restore. */
abstract fun createBackupRestoreEntities(): List<BackupRestoreEntity>
@@ -76,7 +78,7 @@
data: BackupDataOutput,
newState: ParcelFileDescriptor,
) {
- oldState.readEntityStates(entityStates)
+ readEntityStates(oldState, entityStates)
val backupContext = BackupContext(data)
if (!enableBackup(backupContext)) {
Log.i(LOG_TAG, "[$name] Backup disabled")
@@ -94,7 +96,10 @@
val codec = entity.codec() ?: defaultCodec()
val result =
try {
- entity.backup(backupContext, wrapBackupOutputStream(codec, checkedOutputStream))
+ // MUST close to flush all data
+ wrapBackupOutputStream(codec, checkedOutputStream).use {
+ entity.backup(backupContext, it)
+ }
} catch (exception: Exception) {
Log.e(LOG_TAG, "[$name] Fail to backup entity $key", exception)
continue
@@ -191,9 +196,13 @@
/** Callbacks when restore finished. */
open fun onRestoreFinished() {}
- private fun ParcelFileDescriptor?.readEntityStates(state: MutableScatterMap<String, Long>) {
+ @VisibleForTesting
+ internal fun readEntityStates(
+ parcelFileDescriptor: ParcelFileDescriptor?,
+ state: MutableScatterMap<String, Long>,
+ ) {
state.clear()
- if (this == null) return
+ val fileDescriptor = parcelFileDescriptor?.fileDescriptor ?: return
// do not close the streams
val fileInputStream = FileInputStream(fileDescriptor)
val dataInputStream = DataInputStream(fileInputStream)
@@ -233,6 +242,7 @@
dataOutputStream.writeUTF(key)
dataOutputStream.writeLong(value)
}
+ dataOutputStream.flush()
} catch (exception: Exception) {
Log.e(LOG_TAG, "[$name] Fail to write state file", exception)
}
@@ -241,7 +251,7 @@
}
companion object {
- private const val STATE_VERSION: Byte = 0
+ internal const val STATE_VERSION: Byte = 0
/** Checksum for entity backup data. */
fun createChecksum(): Checksum = CRC32()
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
index cfdcaff..8242347 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
@@ -21,23 +21,32 @@
import android.app.backup.BackupManager
import android.content.Context
import android.util.Log
+import androidx.annotation.VisibleForTesting
import com.google.common.util.concurrent.MoreExecutors
import java.util.concurrent.ConcurrentHashMap
/** Manager of [BackupRestoreStorage]. */
class BackupRestoreStorageManager private constructor(private val application: Application) {
- private val storageWrappers = ConcurrentHashMap<String, StorageWrapper>()
+ @VisibleForTesting internal val storageWrappers = ConcurrentHashMap<String, StorageWrapper>()
private val executor = MoreExecutors.directExecutor()
/**
* Adds all the registered [BackupRestoreStorage] as the helpers of given [BackupAgentHelper].
*
- * All [BackupRestoreFileStorage]s will be wrapped as a single [BackupRestoreFileArchiver].
+ * All [BackupRestoreFileStorage]s will be wrapped as a single [BackupRestoreFileArchiver],
+ * specify [fileArchiverName] to avoid key prefix conflict if needed.
*
+ * @param backupAgentHelper backup agent helper to add helpers
+ * @param fileArchiverName key prefix of the [BackupRestoreFileArchiver], the value must not be
+ * changed in future
* @see BackupAgentHelper.addHelper
*/
- fun addBackupAgentHelpers(backupAgentHelper: BackupAgentHelper) {
+ @JvmOverloads
+ fun addBackupAgentHelpers(
+ backupAgentHelper: BackupAgentHelper,
+ fileArchiverName: String = "file_archiver",
+ ) {
val fileStorages = mutableListOf<BackupRestoreFileStorage>()
for ((keyPrefix, storageWrapper) in storageWrappers) {
val storage = storageWrapper.storage
@@ -48,7 +57,7 @@
}
}
// Always add file archiver even fileStorages is empty to handle forward compatibility
- val fileArchiver = BackupRestoreFileArchiver(application, fileStorages)
+ val fileArchiver = BackupRestoreFileArchiver(application, fileStorages, fileArchiverName)
backupAgentHelper.addHelper(fileArchiver.name, fileArchiver)
}
@@ -106,7 +115,8 @@
/** Returns storage with given name, exception is raised if not found. */
fun getOrThrow(name: String): BackupRestoreStorage = storageWrappers[name]!!.storage
- private inner class StorageWrapper(val storage: BackupRestoreStorage) :
+ @VisibleForTesting
+ internal inner class StorageWrapper(val storage: BackupRestoreStorage) :
Observer, KeyedObserver<Any?> {
init {
when (storage) {
@@ -139,7 +149,7 @@
LOG_TAG,
"Notify BackupManager dataChanged: storage=$name key=$key reason=$reason"
)
- BackupManager.dataChanged(application.packageName)
+ BackupManager(application).dataChanged()
}
fun removeObserver() {
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
index 0c1b417..9f9c0d8 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
@@ -20,9 +20,11 @@
import android.content.SharedPreferences
import android.os.Build
import android.util.Log
-import androidx.core.content.ContextCompat
+import androidx.annotation.VisibleForTesting
import java.io.File
+private fun defaultVerbose() = Build.TYPE == "eng"
+
/**
* [SharedPreferences] based storage.
*
@@ -43,24 +45,35 @@
* @param verbose Verbose logging on key/value pairs during backup/restore. Enable for dev only!
* @param filter Filter of key/value pairs for backup and restore.
*/
-class SharedPreferencesStorage
+open class SharedPreferencesStorage
@JvmOverloads
constructor(
context: Context,
override val name: String,
- mode: Int,
- private val verbose: Boolean = (Build.TYPE == "eng"),
+ @get:VisibleForTesting internal val sharedPreferences: SharedPreferences,
+ private val codec: BackupCodec? = null,
+ private val verbose: Boolean = defaultVerbose(),
private val filter: (String, Any?) -> Boolean = { _, _ -> true },
) :
BackupRestoreFileStorage(context, context.getSharedPreferencesFilePath(name)),
KeyedObservable<String> by KeyedDataObservable() {
- private val sharedPreferences = context.getSharedPreferences(name, mode)
+ @JvmOverloads
+ constructor(
+ context: Context,
+ name: String,
+ mode: Int,
+ codec: BackupCodec? = null,
+ verbose: Boolean = defaultVerbose(),
+ filter: (String, Any?) -> Boolean = { _, _ -> true },
+ ) : this(context, name, context.getSharedPreferences(name, mode), codec, verbose, filter)
/** Name of the intermediate SharedPreferences. */
- private val intermediateName: String
+ @VisibleForTesting
+ internal val intermediateName: String
get() = "_br_$name"
+ @Suppress("DEPRECATION")
private val intermediateSharedPreferences: SharedPreferences
get() {
// use MODE_MULTI_PROCESS to ensure a reload
@@ -82,12 +95,15 @@
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
}
+ override fun defaultCodec() = codec ?: super.defaultCodec()
+
override val backupFile: File
// use a different file to avoid multi-thread file write
get() = context.getSharedPreferencesFile(intermediateName)
override fun prepareBackup(file: File) {
- val editor = intermediateSharedPreferences.merge(sharedPreferences.all, "Backup")
+ val editor =
+ mergeSharedPreferences(intermediateSharedPreferences, sharedPreferences.all, "Backup")
// commit to ensure data is write to disk synchronously
if (!editor.commit()) {
Log.w(LOG_TAG, "[$name] fail to commit")
@@ -104,8 +120,8 @@
// observers consistently once restore finished.
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferencesListener)
val restored = intermediateSharedPreferences
- val editor = sharedPreferences.merge(restored.all, "Restore")
- editor.apply() // apply to avoid blocking
+ val editor = mergeSharedPreferences(sharedPreferences, restored.all, "Restore")
+ editor.commit() // commit to avoid race condition
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
// clear the intermediate SharedPreferences
restored.delete(intermediateName)
@@ -115,7 +131,7 @@
if (deleteSharedPreferences(name)) {
Log.i(LOG_TAG, "SharedPreferences $name deleted")
} else {
- edit().clear().apply()
+ edit().clear().commit() // commit to avoid potential race condition
}
}
@@ -126,11 +142,13 @@
false
}
- private fun SharedPreferences.merge(
+ @VisibleForTesting
+ internal open fun mergeSharedPreferences(
+ sharedPreferences: SharedPreferences,
entries: Map<String, Any?>,
- operation: String
+ operation: String,
): SharedPreferences.Editor {
- val editor = edit()
+ val editor = sharedPreferences.edit()
for ((key, value) in entries) {
if (!filter.invoke(key, value)) {
if (verbose) Log.v(LOG_TAG, "[$name] $operation skips $key=$value")
@@ -184,7 +202,7 @@
companion object {
private fun Context.getSharedPreferencesFilePath(name: String): String {
val file = getSharedPreferencesFile(name)
- return file.relativeTo(ContextCompat.getDataDir(this)!!).toString()
+ return file.relativeTo(dataDirCompat).toString()
}
/** Returns the absolute path of shared preferences file. */
diff --git a/packages/SettingsLib/DataStore/tests/Android.bp b/packages/SettingsLib/DataStore/tests/Android.bp
index 8770dfa..5d000eb 100644
--- a/packages/SettingsLib/DataStore/tests/Android.bp
+++ b/packages/SettingsLib/DataStore/tests/Android.bp
@@ -9,11 +9,16 @@
android_robolectric_test {
name: "SettingsLibDataStoreTest",
- srcs: ["src/**/*"],
+ srcs: [
+ ":SettingsLibDataStore-srcs", // b/240432457
+ "src/**/*",
+ ],
static_libs: [
- "SettingsLibDataStore",
+ "androidx.collection_collection-ktx",
+ "androidx.core_core-ktx",
"androidx.test.ext.junit",
"guava",
+ "kotlin-test",
"mockito-robolectric-prebuilt", // mockito deps order matters!
"mockito-kotlin2",
],
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupCodecTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupCodecTest.kt
new file mode 100644
index 0000000..867831b
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupCodecTest.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import kotlin.random.Random
+import kotlin.test.assertFailsWith
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests of [BackupCodec]. */
+@RunWith(AndroidJUnit4::class)
+class BackupCodecTest {
+ @Test
+ fun name() {
+ val names = mutableSetOf<String>()
+ for (codec in allCodecs()) {
+ assertThat(names).doesNotContain(codec.name)
+ names.add(codec.name)
+ }
+ }
+
+ @Test
+ fun fromId() {
+ for (codec in allCodecs()) {
+ assertThat(BackupCodec.fromId(codec.id)).isInstanceOf(codec::class.java)
+ }
+ }
+
+ @Test
+ fun fromId_unknownId() {
+ assertFailsWith(IllegalArgumentException::class) { BackupCodec.fromId(-1) }
+ }
+
+ @Test
+ fun encode_decode() {
+ val random = Random.Default
+ fun test(codec: BackupCodec, size: Int) {
+ val data = random.nextBytes(size)
+
+ // encode
+ val outputStream = ByteArrayOutputStream()
+ codec.encode(outputStream).use { it.write(data) }
+
+ // decode
+ val inputStream = ByteArrayInputStream(outputStream.toByteArray())
+ val result = codec.decode(inputStream).use { it.readBytes() }
+
+ assertWithMessage("$size bytes: $data").that(result).isEqualTo(data)
+ }
+
+ for (codec in allCodecs()) {
+ test(codec, 0)
+ repeat(10) { test(codec, random.nextInt(1, 1024)) }
+ }
+ }
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreContextTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreContextTest.kt
new file mode 100644
index 0000000..911665a
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreContextTest.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.app.backup.BackupDataOutput
+import android.os.Build
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+
+/** Tests of [BackupContext] and [RestoreContext]. */
+@RunWith(AndroidJUnit4::class)
+class BackupRestoreContextTest {
+ @Test
+ fun backupContext_quota() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
+ val data = mock<BackupDataOutput> { on { quota } doReturn 10L }
+ assertThat(BackupContext(data).quota).isEqualTo(10)
+ }
+
+ @Test
+ fun backupContext_transportFlags() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return
+ val data = mock<BackupDataOutput> { on { transportFlags } doReturn 5 }
+ assertThat(BackupContext(data).transportFlags).isEqualTo(5)
+ }
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileArchiverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileArchiverTest.kt
new file mode 100644
index 0000000..6cce453
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileArchiverTest.kt
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.app.Application
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.IOException
+import java.io.InputStream
+import kotlin.random.Random
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+/** Tests of [BackupRestoreFileArchiver]. */
+@RunWith(AndroidJUnit4::class)
+class BackupRestoreFileArchiverTest {
+ private val random = Random.Default
+ private val application: Application = getApplicationContext()
+ @get:Rule val temporaryFolder = TemporaryFolder(application.dataDirCompat)
+
+ @Test
+ fun createBackupRestoreEntities() {
+ val fileStorages = mutableListOf<BackupRestoreFileStorage>()
+ for (count in 0 until 3) {
+ val fileArchiver = BackupRestoreFileArchiver(application, fileStorages, "")
+ fileArchiver.createBackupRestoreEntities().apply {
+ assertThat(this).hasSize(fileStorages.size)
+ for (index in 0 until count) {
+ assertThat(get(index).key).isEqualTo(fileStorages[index].storageFilePath)
+ }
+ }
+ fileStorages.add(FileStorage("storage", "path$count"))
+ }
+ }
+
+ @Test
+ fun wrapBackupOutputStream() {
+ val fileArchiver = BackupRestoreFileArchiver(application, listOf(), "")
+ val outputStream = ByteArrayOutputStream()
+ assertThat(fileArchiver.wrapBackupOutputStream(BackupZipCodec.BEST_SPEED, outputStream))
+ .isSameInstanceAs(outputStream)
+ }
+
+ @Test
+ fun wrapRestoreInputStream() {
+ val fileArchiver = BackupRestoreFileArchiver(application, listOf(), "")
+ val inputStream = ByteArrayInputStream(byteArrayOf())
+ assertThat(fileArchiver.wrapRestoreInputStream(BackupZipCodec.BEST_SPEED, inputStream))
+ .isSameInstanceAs(inputStream)
+ }
+
+ @Test
+ fun restoreEntity_disabled() {
+ val file = temporaryFolder.newFile()
+ val key = file.name
+ val fileStorage = FileStorage("fs", key, restoreEnabled = false)
+
+ BackupRestoreFileArchiver(application, listOf(fileStorage), "archiver").apply {
+ restoreEntity(newBackupDataInputStream(key, byteArrayOf()))
+ assertThat(entityStates.asMap()).isEmpty()
+ }
+ }
+
+ @Test
+ fun restoreEntity_raiseIOException() {
+ val key = "key"
+ val fileStorage = FileStorage("fs", key)
+ BackupRestoreFileArchiver(application, listOf(fileStorage), "archiver").apply {
+ restoreEntity(newBackupDataInputStream(key, byteArrayOf(), IOException()))
+ assertThat(entityStates.asMap()).isEmpty()
+ }
+ }
+
+ @Test
+ fun restoreEntity_onRestoreFinished_raiseException() {
+ val key = "key"
+ val fileStorage = FileStorage("fs", key, restoreException = IllegalStateException())
+ BackupRestoreFileArchiver(application, listOf(fileStorage), "archiver").apply {
+ val data = random.nextBytes(random.nextInt(10))
+ val outputStream = ByteArrayOutputStream()
+ fileStorage.wrapBackupOutputStream(fileStorage.defaultCodec(), outputStream).use {
+ it.write(data)
+ }
+ val payload = outputStream.toByteArray()
+ restoreEntity(newBackupDataInputStream(key, payload))
+ assertThat(entityStates.asMap()).isEmpty()
+ }
+ }
+
+ @Test
+ fun restoreEntity_forwardCompatibility() {
+ val key = "key"
+ val fileStorage = FileStorage("fs", key)
+ for (codec in allCodecs()) {
+ BackupRestoreFileArchiver(application, listOf(), "archiver").apply {
+ val data = random.nextBytes(random.nextInt(MAX_DATA_SIZE))
+ val outputStream = ByteArrayOutputStream()
+ fileStorage.wrapBackupOutputStream(codec, outputStream).use { it.write(data) }
+ val payload = outputStream.toByteArray()
+
+ restoreEntity(newBackupDataInputStream(key, payload))
+
+ assertThat(entityStates.asMap()).apply {
+ hasSize(1)
+ containsKey(key)
+ }
+ assertThat(fileStorage.restoreFile.readBytes()).isEqualTo(data)
+ }
+ }
+ }
+
+ @Test
+ fun restoreEntity() {
+ val folder = File(application.dataDirCompat, "backup")
+ val file = File(folder, "file")
+ val key = "${folder.name}${File.separator}${file.name}"
+ fun test(codec: BackupCodec, size: Int) {
+ val fileStorage = FileStorage("fs", key, if (size % 2 == 0) codec else null)
+ val data = random.nextBytes(size)
+ val outputStream = ByteArrayOutputStream()
+ fileStorage.wrapBackupOutputStream(codec, outputStream).use { it.write(data) }
+ val payload = outputStream.toByteArray()
+
+ val fileArchiver =
+ BackupRestoreFileArchiver(application, listOf(fileStorage), "archiver")
+ fileArchiver.restoreEntity(newBackupDataInputStream(key, payload))
+
+ assertThat(fileArchiver.entityStates.asMap()).apply {
+ hasSize(1)
+ containsKey(key)
+ }
+ assertThat(file.readBytes()).isEqualTo(data)
+ }
+
+ for (codec in allCodecs()) {
+ for (size in 0 until 100) test(codec, size)
+ repeat(10) { test(codec, random.nextInt(100, MAX_DATA_SIZE)) }
+ }
+ }
+
+ @Test
+ fun onRestoreFinished() {
+ val fileStorage = mock<BackupRestoreFileStorage>()
+ val fileArchiver = BackupRestoreFileArchiver(application, listOf(fileStorage), "")
+
+ fileArchiver.onRestoreFinished()
+
+ verify(fileStorage).onRestoreFinished()
+ }
+
+ @Test
+ fun toBackupRestoreEntity_backup_disabled() {
+ val context = BackupContext(mock())
+ val fileStorage =
+ mock<BackupRestoreFileStorage> { on { enableBackup(context) } doReturn false }
+
+ assertThat(fileStorage.toBackupRestoreEntity().backup(context, ByteArrayOutputStream()))
+ .isEqualTo(EntityBackupResult.INTACT)
+
+ verify(fileStorage, never()).prepareBackup(any())
+ }
+
+ @Test
+ fun toBackupRestoreEntity_backup_fileNotExist() {
+ val context = BackupContext(mock())
+ val file = File("NotExist")
+ val fileStorage =
+ mock<BackupRestoreFileStorage> {
+ on { enableBackup(context) } doReturn true
+ on { backupFile } doReturn file
+ }
+
+ assertThat(fileStorage.toBackupRestoreEntity().backup(context, ByteArrayOutputStream()))
+ .isEqualTo(EntityBackupResult.DELETE)
+
+ verify(fileStorage).prepareBackup(file)
+ verify(fileStorage, never()).defaultCodec()
+ }
+
+ @Test
+ fun toBackupRestoreEntity_backup() {
+ val context = BackupContext(mock())
+ val file = temporaryFolder.newFile()
+
+ fun test(codec: BackupCodec, size: Int) {
+ val data = random.nextBytes(size)
+ file.outputStream().use { it.write(data) }
+
+ val outputStream = ByteArrayOutputStream()
+ val fileStorage =
+ mock<BackupRestoreFileStorage> {
+ on { enableBackup(context) } doReturn true
+ on { backupFile } doReturn file
+ on { defaultCodec() } doReturn codec
+ on { wrapBackupOutputStream(any(), any()) }.thenCallRealMethod()
+ on { wrapRestoreInputStream(any(), any()) }.thenCallRealMethod()
+ on { prepareBackup(any()) }.thenCallRealMethod()
+ on { onBackupFinished(any()) }.thenCallRealMethod()
+ }
+
+ assertThat(fileStorage.toBackupRestoreEntity().backup(context, outputStream))
+ .isEqualTo(EntityBackupResult.UPDATE)
+
+ verify(fileStorage).prepareBackup(file)
+ verify(fileStorage).onBackupFinished(file)
+
+ val decodedData =
+ fileStorage
+ .wrapRestoreInputStream(codec, ByteArrayInputStream(outputStream.toByteArray()))
+ .readBytes()
+ assertThat(decodedData).isEqualTo(data)
+ }
+
+ for (codec in allCodecs()) {
+ // test small data to ensure correctness
+ for (size in 0 until 100) test(codec, size)
+ repeat(10) { test(codec, random.nextInt(100, MAX_DATA_SIZE)) }
+ }
+ }
+
+ @Test
+ fun toBackupRestoreEntity_restore() {
+ val restoreContext = RestoreContext("storage")
+ val inputStream =
+ object : InputStream() {
+ override fun read() = throw IllegalStateException()
+
+ override fun read(b: ByteArray, off: Int, len: Int) = throw IllegalStateException()
+ }
+ FileStorage("storage", "path").toBackupRestoreEntity().restore(restoreContext, inputStream)
+ }
+
+ private open class FileStorage(
+ override val name: String,
+ filePath: String,
+ private val codec: BackupCodec? = null,
+ private val restoreEnabled: Boolean? = null,
+ private val restoreException: Exception? = null,
+ ) : BackupRestoreFileStorage(getApplicationContext(), filePath) {
+
+ override fun defaultCodec() = codec ?: super.defaultCodec()
+
+ override fun enableRestore() = restoreEnabled ?: super.enableRestore()
+
+ override fun onRestoreFinished(file: File) {
+ super.onRestoreFinished(file)
+ if (restoreException != null) throw restoreException
+ }
+ }
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileStorageTest.kt
new file mode 100644
index 0000000..422273d
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileStorageTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.app.Application
+import android.os.Build
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import kotlin.test.assertFailsWith
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests of [BackupRestoreFileStorage]. */
+@RunWith(AndroidJUnit4::class)
+class BackupRestoreFileStorageTest {
+ private val application: Application = getApplicationContext()
+
+ @Test
+ fun dataDirCompat() {
+ val expected =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ application.dataDir
+ } else {
+ File(application.applicationInfo.dataDir)
+ }
+ assertThat(application.dataDirCompat).isEqualTo(expected)
+ }
+
+ @Test
+ fun backupFile() {
+ assertThat(FileStorage("path").backupFile.toString())
+ .startsWith(application.dataDirCompat.toString())
+ }
+
+ @Test
+ fun restoreFile() {
+ FileStorage("path").apply { assertThat(restoreFile).isEqualTo(backupFile) }
+ }
+
+ @Test
+ fun checkFilePaths() {
+ FileStorage("path").checkFilePaths()
+ }
+
+ @Test
+ fun checkFilePaths_emptyFilePath() {
+ assertFailsWith(IllegalArgumentException::class) { FileStorage("").checkFilePaths() }
+ }
+
+ @Test
+ fun checkFilePaths_absoluteFilePath() {
+ assertFailsWith(IllegalArgumentException::class) {
+ FileStorage("${File.separatorChar}file").checkFilePaths()
+ }
+ }
+
+ @Test
+ fun checkFilePaths_backupFile() {
+ assertFailsWith(IllegalArgumentException::class) {
+ FileStorage("path", fileForBackup = File("path")).checkFilePaths()
+ }
+ }
+
+ @Test
+ fun checkFilePaths_restoreFile() {
+ assertFailsWith(IllegalArgumentException::class) {
+ FileStorage("path", fileForRestore = File("path")).checkFilePaths()
+ }
+ }
+
+ @Test
+ fun createBackupRestoreEntities() {
+ assertThat(FileStorage("path").createBackupRestoreEntities()).isEmpty()
+ }
+
+ private class FileStorage(
+ filePath: String,
+ val fileForBackup: File? = null,
+ val fileForRestore: File? = null,
+ ) : BackupRestoreFileStorage(getApplicationContext(), filePath) {
+ override val name = "storage"
+
+ override val backupFile: File
+ get() = fileForBackup ?: super.backupFile
+
+ override val restoreFile: File
+ get() = fileForRestore ?: super.restoreFile
+ }
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
new file mode 100644
index 0000000..d8f5028
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.app.Application
+import android.app.backup.BackupAgentHelper
+import android.app.backup.BackupHelper
+import android.app.backup.BackupManager
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import kotlin.test.assertFailsWith
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+import org.robolectric.Shadows
+import org.robolectric.shadows.ShadowBackupManager
+
+/** Tests of [BackupRestoreStorageManager]. */
+@RunWith(AndroidJUnit4::class)
+class BackupRestoreStorageManagerTest {
+ private val application: Application = getApplicationContext()
+ private val manager = BackupRestoreStorageManager.getInstance(application)
+ private val fileStorage = FileStorage("fileStorage")
+ private val keyedStorage = KeyedStorage("keyedStorage")
+
+ private val storage1 = mock<ObservableBackupRestoreStorage> { on { name } doReturn "1" }
+ private val storage2 = mock<ObservableBackupRestoreStorage> { on { name } doReturn "1" }
+
+ @After
+ fun tearDown() {
+ manager.removeAll()
+ ShadowBackupManager.reset()
+ }
+
+ @Test
+ fun getInstance() {
+ assertThat(BackupRestoreStorageManager.getInstance(application)).isSameInstanceAs(manager)
+ }
+
+ @Test
+ fun addBackupAgentHelpers() {
+ val fs = FileStorage("fs")
+ manager.add(keyedStorage, fileStorage, storage1, fs)
+ val backupAgentHelper = DummyBackupAgentHelper()
+ manager.addBackupAgentHelpers(backupAgentHelper)
+ backupAgentHelper.backupHelpers.apply {
+ assertThat(size).isEqualTo(3)
+ assertThat(remove(keyedStorage.name)).isSameInstanceAs(keyedStorage)
+ assertThat(remove(storage1.name)).isSameInstanceAs(storage1)
+ val fileArchiver = entries.first().value as BackupRestoreFileArchiver
+ assertThat(fileArchiver.fileStorages.toSet()).containsExactly(fs, fileStorage)
+ }
+ }
+
+ @Test
+ fun addBackupAgentHelpers_withoutFileStorage() {
+ manager.add(keyedStorage, storage1)
+ val backupAgentHelper = DummyBackupAgentHelper()
+ manager.addBackupAgentHelpers(backupAgentHelper)
+ backupAgentHelper.backupHelpers.apply {
+ assertThat(size).isEqualTo(3)
+ assertThat(remove(keyedStorage.name)).isSameInstanceAs(keyedStorage)
+ assertThat(remove(storage1.name)).isSameInstanceAs(storage1)
+ val fileArchiver = entries.first().value as BackupRestoreFileArchiver
+ assertThat(fileArchiver.fileStorages).isEmpty()
+ }
+ }
+
+ @Test
+ fun add() {
+ manager.add(keyedStorage, fileStorage, storage1)
+ assertThat(manager.storageWrappers).apply {
+ hasSize(3)
+ containsKey(keyedStorage.name)
+ containsKey(fileStorage.name)
+ containsKey(storage1.name)
+ }
+ }
+
+ @Test
+ fun add_identicalName() {
+ manager.add(storage1)
+ assertFailsWith(IllegalStateException::class) { manager.add(storage1) }
+ assertFailsWith(IllegalStateException::class) { manager.add(storage2) }
+ }
+
+ @Test
+ fun add_nonObservable() {
+ assertFailsWith(IllegalArgumentException::class) {
+ manager.add(mock<BackupRestoreStorage>())
+ }
+ }
+
+ @Test
+ fun removeAll() {
+ add()
+ manager.removeAll()
+ assertThat(manager.storageWrappers).isEmpty()
+ }
+
+ @Test
+ fun remove() {
+ manager.add(keyedStorage, fileStorage)
+ assertThat(manager.remove(storage1.name)).isNull()
+ assertThat(manager.remove(keyedStorage.name)).isSameInstanceAs(keyedStorage)
+ assertThat(manager.remove(fileStorage.name)).isSameInstanceAs(fileStorage)
+ }
+
+ @Test
+ fun get() {
+ manager.add(keyedStorage, fileStorage)
+ assertThat(manager.get(storage1.name)).isNull()
+ assertThat(manager.get(keyedStorage.name)).isSameInstanceAs(keyedStorage)
+ assertThat(manager.get(fileStorage.name)).isSameInstanceAs(fileStorage)
+ }
+
+ @Test
+ fun getOrThrow() {
+ manager.add(keyedStorage, fileStorage)
+ assertFailsWith(NullPointerException::class) { manager.getOrThrow(storage1.name) }
+ assertThat(manager.getOrThrow(keyedStorage.name)).isSameInstanceAs(keyedStorage)
+ assertThat(manager.getOrThrow(fileStorage.name)).isSameInstanceAs(fileStorage)
+ }
+
+ @Test
+ fun notifyRestoreFinished() {
+ manager.add(keyedStorage, fileStorage)
+ val keyedObserver = mock<KeyedObserver<String>>()
+ val anyKeyObserver = mock<KeyedObserver<String?>>()
+ val observer = mock<Observer>()
+ val executor = directExecutor()
+ keyedStorage.addObserver("key", keyedObserver, executor)
+ keyedStorage.addObserver(anyKeyObserver, executor)
+ fileStorage.addObserver(observer, executor)
+
+ manager.onRestoreFinished()
+
+ verify(keyedObserver).onKeyChanged("key", ChangeReason.RESTORE)
+ verify(anyKeyObserver).onKeyChanged(null, ChangeReason.RESTORE)
+ verify(observer).onChanged(ChangeReason.RESTORE)
+ if (isRobolectric()) {
+ Shadows.shadowOf(BackupManager(application)).apply {
+ assertThat(isDataChanged).isFalse()
+ assertThat(dataChangedCount).isEqualTo(0)
+ }
+ }
+ }
+
+ @Test
+ fun notifyBackupManager() {
+ manager.add(keyedStorage, fileStorage)
+ val keyedObserver = mock<KeyedObserver<String>>()
+ val anyKeyObserver = mock<KeyedObserver<String?>>()
+ val observer = mock<Observer>()
+ val executor = directExecutor()
+ keyedStorage.addObserver("key", keyedObserver, executor)
+ keyedStorage.addObserver(anyKeyObserver, executor)
+ fileStorage.addObserver(observer, executor)
+
+ val backupManager =
+ if (isRobolectric()) Shadows.shadowOf(BackupManager(application)) else null
+ backupManager?.apply {
+ assertThat(isDataChanged).isFalse()
+ assertThat(dataChangedCount).isEqualTo(0)
+ }
+
+ fileStorage.notifyChange(ChangeReason.UPDATE)
+ verify(observer).onChanged(ChangeReason.UPDATE)
+ verify(keyedObserver, never()).onKeyChanged(any(), any())
+ verify(anyKeyObserver, never()).onKeyChanged(any(), any())
+ reset(observer)
+ backupManager?.apply {
+ assertThat(isDataChanged).isTrue()
+ assertThat(dataChangedCount).isEqualTo(1)
+ }
+
+ keyedStorage.notifyChange("key", ChangeReason.DELETE)
+ verify(observer, never()).onChanged(any())
+ verify(keyedObserver).onKeyChanged("key", ChangeReason.DELETE)
+ verify(anyKeyObserver).onKeyChanged("key", ChangeReason.DELETE)
+ backupManager?.apply {
+ assertThat(isDataChanged).isTrue()
+ assertThat(dataChangedCount).isEqualTo(2)
+ }
+ reset(keyedObserver)
+
+ // backup manager is not notified for restore event
+ fileStorage.notifyChange(ChangeReason.RESTORE)
+ keyedStorage.notifyChange("key", ChangeReason.RESTORE)
+ verify(observer).onChanged(ChangeReason.RESTORE)
+ verify(keyedObserver).onKeyChanged("key", ChangeReason.RESTORE)
+ verify(anyKeyObserver).onKeyChanged("key", ChangeReason.RESTORE)
+ backupManager?.apply {
+ assertThat(isDataChanged).isTrue()
+ assertThat(dataChangedCount).isEqualTo(2)
+ }
+ }
+
+ private class KeyedStorage(override val name: String) :
+ BackupRestoreStorage(), KeyedObservable<String> by KeyedDataObservable() {
+
+ override fun createBackupRestoreEntities(): List<BackupRestoreEntity> = listOf()
+ }
+
+ private class FileStorage(override val name: String) :
+ BackupRestoreFileStorage(getApplicationContext(), "file"), Observable by DataObservable()
+
+ private class DummyBackupAgentHelper : BackupAgentHelper() {
+ val backupHelpers = mutableMapOf<String, BackupHelper>()
+
+ override fun addHelper(keyPrefix: String, helper: BackupHelper) {
+ backupHelpers[keyPrefix] = helper
+ }
+ }
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt
new file mode 100644
index 0000000..99998ff
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.app.backup.BackupAgentHelper
+import android.app.backup.BackupDataInput
+import android.app.backup.BackupDataInputStream
+import android.app.backup.BackupDataOutput
+import android.os.ParcelFileDescriptor
+import android.os.ParcelFileDescriptor.MODE_APPEND
+import android.os.ParcelFileDescriptor.MODE_READ_ONLY
+import android.os.ParcelFileDescriptor.MODE_WRITE_ONLY
+import androidx.collection.MutableScatterMap
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import java.io.DataOutputStream
+import java.io.File
+import java.io.FileDescriptor
+import java.io.FileOutputStream
+import java.io.InputStream
+import java.io.OutputStream
+import kotlin.random.Random
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+
+/** Tests of [BackupRestoreStorage]. */
+@RunWith(AndroidJUnit4::class)
+class BackupRestoreStorageTest {
+ @get:Rule val temporaryFolder = TemporaryFolder()
+
+ private val entity1 = Entity("key1", "value1".toByteArray())
+ private val entity1NoOpCodec = Entity("key1", "value1".toByteArray(), BackupNoOpCodec())
+ private val entity2 = Entity("key2", "value2".toByteArray(), BackupZipCodec.BEST_SPEED)
+
+ @Test
+ fun performBackup_disabled() {
+ val storage = spy(TestStorage().apply { enabled = false })
+ val unused = performBackup { data, newState -> storage.performBackup(null, data, newState) }
+ verify(storage, never()).createBackupRestoreEntities()
+ assertThat(storage.entities).isNull()
+ assertThat(storage.entityStates.size).isEqualTo(0)
+ }
+
+ @Test
+ fun performBackup_enabled() {
+ val storage = spy(TestStorage())
+ val unused = performBackup { data, newState -> storage.performBackup(null, data, newState) }
+ verify(storage).createBackupRestoreEntities()
+ assertThat(storage.entities).isNull()
+ assertThat(storage.entityStates.size).isEqualTo(0)
+ }
+
+ @Test
+ fun performBackup_entityBackupWithException() {
+ val entity =
+ mock<BackupRestoreEntity> {
+ on { key } doReturn ""
+ on { backup(any(), any()) } doThrow IllegalStateException()
+ }
+ val storage = TestStorage(entity, entity1)
+
+ val (_, stateFile) =
+ performBackup { data, newState -> storage.performBackup(null, data, newState) }
+
+ assertThat(storage.readEntityStates(stateFile)).apply {
+ hasSize(1)
+ containsKey(entity1.key)
+ }
+ }
+
+ @Test
+ fun performBackup_update_unchanged() {
+ performBackupTest({}) { entityStates, newEntityStates ->
+ assertThat(entityStates).isEqualTo(newEntityStates)
+ }
+ }
+
+ @Test
+ fun performBackup_intact() {
+ performBackupTest({ entity1.backupResult = EntityBackupResult.INTACT }) {
+ entityStates,
+ newEntityStates ->
+ assertThat(entityStates).isEqualTo(newEntityStates)
+ }
+ }
+
+ @Test
+ fun performBackup_delete() {
+ performBackupTest({ entity1.backupResult = EntityBackupResult.DELETE }) { _, newEntityStates
+ ->
+ assertThat(newEntityStates.size).isEqualTo(1)
+ assertThat(newEntityStates).containsKey(entity2.key)
+ }
+ }
+
+ private fun performBackupTest(
+ update: () -> Unit,
+ verification: (Map<String, Long>, Map<String, Long>) -> Unit,
+ ) {
+ val storage = TestStorage(entity1, entity2)
+ val (_, stateFile) =
+ performBackup { data, newState -> storage.performBackup(null, data, newState) }
+
+ val entityStates = storage.readEntityStates(stateFile)
+ assertThat(entityStates).apply {
+ hasSize(2)
+ containsKey(entity1.key)
+ containsKey(entity2.key)
+ }
+
+ update.invoke()
+ val (_, newStateFile) =
+ performBackup { data, newState ->
+ stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use {
+ storage.performBackup(it, data, newState)
+ }
+ }
+ verification.invoke(entityStates, storage.readEntityStates(newStateFile))
+ }
+
+ @Test
+ fun restoreEntity_disabled() {
+ val storage = spy(TestStorage().apply { enabled = false })
+ temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use {
+ storage.restoreEntity(it.toBackupDataInputStream())
+ }
+ verify(storage, never()).createBackupRestoreEntities()
+ assertThat(storage.entities).isNull()
+ assertThat(storage.entityStates.size).isEqualTo(0)
+ }
+
+ @Test
+ fun restoreEntity_entityNotFound() {
+ val storage = TestStorage()
+ temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use {
+ val backupDataInputStream = it.toBackupDataInputStream()
+ backupDataInputStream.setKey("")
+ storage.restoreEntity(backupDataInputStream)
+ }
+ }
+
+ @Test
+ fun restoreEntity_exception() {
+ val storage = TestStorage(entity1)
+ temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use {
+ val backupDataInputStream = it.toBackupDataInputStream()
+ backupDataInputStream.setKey(entity1.key)
+ storage.restoreEntity(backupDataInputStream)
+ }
+ }
+
+ @Test
+ fun restoreEntity_codecChanged() {
+ assertThat(entity1.codec()).isNotEqualTo(entity1NoOpCodec.codec())
+ backupAndRestore(entity1) { _, data ->
+ TestStorage(entity1NoOpCodec).apply { restoreEntity(data) }
+ }
+ assertThat(entity1.data).isEqualTo(entity1NoOpCodec.restoredData)
+ }
+
+ @Test
+ fun restoreEntity() {
+ val random = Random.Default
+ fun test(codec: BackupCodec, size: Int) {
+ val entity = Entity("key", random.nextBytes(size), codec)
+ backupAndRestore(entity)
+ entity.verifyRestoredData()
+ }
+ for (codec in allCodecs()) {
+ // test small data to ensure correctness
+ for (size in 0 until 100) test(codec, size)
+ repeat(10) { test(codec, random.nextInt(100, MAX_DATA_SIZE)) }
+ }
+ }
+
+ @Test
+ fun readEntityStates_eof_exception() {
+ val storage = TestStorage()
+ val entityStates = MutableScatterMap<String, Long>()
+ entityStates.put("", 0) // add an item to verify that exiting elements are clear
+ temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use {
+ storage.readEntityStates(it, entityStates)
+ }
+ assertThat(entityStates.size).isEqualTo(0)
+ }
+
+ @Test
+ fun readEntityStates_other_exception() {
+ val storage = TestStorage()
+ val entityStates = MutableScatterMap<String, Long>()
+ entityStates.put("", 0) // add an item to verify that exiting elements are clear
+ temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).apply {
+ close() // cause exception when read state file
+ storage.readEntityStates(this, entityStates)
+ }
+ assertThat(entityStates.size).isEqualTo(0)
+ }
+
+ @Test
+ fun readEntityStates_unknownVersion() {
+ val storage = TestStorage()
+ val stateFile = temporaryFolder.newFile()
+ stateFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use {
+ DataOutputStream(FileOutputStream(it.fileDescriptor))
+ .writeByte(BackupRestoreStorage.STATE_VERSION + 1)
+ }
+ val entityStates = MutableScatterMap<String, Long>()
+ entityStates.put("", 0) // add an item to verify that exiting elements are clear
+ stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use {
+ storage.readEntityStates(it, entityStates)
+ }
+ assertThat(entityStates.size).isEqualTo(0)
+ }
+
+ @Test
+ fun writeNewStateDescription() {
+ val storage = spy(TestStorage())
+ // use read only mode to trigger exception when write state file
+ temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use {
+ storage.writeNewStateDescription(it)
+ }
+ verify(storage).onRestoreFinished()
+ }
+
+ @Test
+ fun backupAndRestore() {
+ val storage = spy(TestStorage(entity1, entity2))
+ val backupAgentHelper = BackupAgentHelper()
+ backupAgentHelper.addHelper(storage.name, storage)
+
+ // backup
+ val (dataFile, stateFile) =
+ performBackup { data, newState -> backupAgentHelper.onBackup(null, data, newState) }
+ storage.verifyFieldsArePurged()
+
+ // verify state
+ val entityStates = MutableScatterMap<String, Long>()
+ entityStates[""] = 1
+ storage.readEntityStates(null, entityStates)
+ assertThat(entityStates.size).isEqualTo(0)
+ stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use {
+ storage.readEntityStates(it, entityStates)
+ }
+ assertThat(entityStates.asMap()).apply {
+ hasSize(2)
+ containsKey(entity1.key)
+ containsKey(entity2.key)
+ }
+ reset(storage)
+
+ // restore
+ val newStateFile = temporaryFolder.newFile()
+ dataFile.toParcelFileDescriptor(MODE_READ_ONLY).use { dataPfd ->
+ newStateFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use {
+ backupAgentHelper.onRestore(dataPfd.toBackupDataInput(), 0, it)
+ }
+ }
+ verify(storage).onRestoreFinished()
+ storage.verifyFieldsArePurged()
+
+ // ShadowBackupDataOutput does not write data to file, so restore is bypassed
+ if (!isRobolectric()) {
+ entity1.verifyRestoredData()
+ entity2.verifyRestoredData()
+ assertThat(entityStates.asMap()).isEqualTo(storage.readEntityStates(newStateFile))
+ }
+ }
+
+ private fun backupAndRestore(
+ entity: BackupRestoreEntity,
+ restoreEntity: (TestStorage, BackupDataInputStream) -> TestStorage = { storage, data ->
+ storage.restoreEntity(data)
+ storage
+ },
+ ) {
+ val storage = TestStorage(entity)
+ val entityKey = argumentCaptor<String>()
+ val entitySize = argumentCaptor<Int>()
+ val entityData = argumentCaptor<ByteArray>()
+ val data = mock<BackupDataOutput>()
+
+ val stateFile = temporaryFolder.newFile()
+ stateFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use {
+ storage.performBackup(null, data, it)
+ }
+ val entityStates = MutableScatterMap<String, Long>()
+ stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use {
+ storage.readEntityStates(it, entityStates)
+ }
+ assertThat(entityStates.size).isEqualTo(1)
+
+ verify(data).writeEntityHeader(entityKey.capture(), entitySize.capture())
+ verify(data).writeEntityData(entityData.capture(), entitySize.capture())
+ assertThat(entityKey.allValues).isEqualTo(listOf(entity.key))
+ assertThat(entityData.allValues).hasSize(1)
+ val payload = entityData.firstValue
+ assertThat(entitySize.allValues).isEqualTo(listOf(payload.size, payload.size))
+
+ val dataFile = temporaryFolder.newFile()
+ dataFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use {
+ FileOutputStream(it.fileDescriptor).write(payload)
+ }
+
+ newBackupDataInputStream(entity.key, payload).apply {
+ restoreEntity.invoke(storage, this).also {
+ assertThat(it.entityStates).isEqualTo(entityStates)
+ }
+ }
+ }
+
+ fun performBackup(backup: (BackupDataOutput, ParcelFileDescriptor) -> Unit): Pair<File, File> {
+ val dataFile = temporaryFolder.newFile()
+ val stateFile = temporaryFolder.newFile()
+ dataFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use { dataPfd ->
+ stateFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use {
+ backup.invoke(dataPfd.toBackupDataOutput(), it)
+ }
+ }
+ return dataFile to stateFile
+ }
+
+ private fun BackupRestoreStorage.verifyFieldsArePurged() {
+ assertThat(entities).isNull()
+ assertThat(entityStates.size).isEqualTo(0)
+ assertThat(entityStates.capacity).isEqualTo(0)
+ }
+
+ private fun BackupRestoreStorage.readEntityStates(stateFile: File): Map<String, Long> {
+ val entityStates = MutableScatterMap<String, Long>()
+ stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use { readEntityStates(it, entityStates) }
+ return entityStates.asMap()
+ }
+
+ private fun File.toParcelFileDescriptor(mode: Int) = ParcelFileDescriptor.open(this, mode)
+
+ private fun ParcelFileDescriptor.toBackupDataOutput() = fileDescriptor.toBackupDataOutput()
+
+ private fun ParcelFileDescriptor.toBackupDataInputStream(): BackupDataInputStream =
+ BackupDataInputStream::class.java.newInstance(toBackupDataInput())
+
+ private fun ParcelFileDescriptor.toBackupDataInput() = fileDescriptor.toBackupDataInput()
+
+ private fun FileDescriptor.toBackupDataOutput(): BackupDataOutput =
+ BackupDataOutput::class.java.newInstance(this)
+
+ private fun FileDescriptor.toBackupDataInput(): BackupDataInput =
+ BackupDataInput::class.java.newInstance(this)
+}
+
+private open class TestStorage(vararg val backupRestoreEntities: BackupRestoreEntity) :
+ ObservableBackupRestoreStorage() {
+ var enabled: Boolean? = null
+
+ override val name
+ get() = "TestBackup"
+
+ override fun createBackupRestoreEntities() = backupRestoreEntities.toList()
+
+ override fun enableBackup(backupContext: BackupContext) =
+ enabled ?: super.enableBackup(backupContext)
+
+ override fun enableRestore() = enabled ?: super.enableRestore()
+}
+
+private class Entity(
+ override val key: String,
+ val data: ByteArray,
+ private val codec: BackupCodec? = null,
+) : BackupRestoreEntity {
+ var restoredData: ByteArray? = null
+ var backupResult = EntityBackupResult.UPDATE
+
+ override fun codec() = codec ?: super.codec()
+
+ override fun backup(
+ backupContext: BackupContext,
+ outputStream: OutputStream,
+ ): EntityBackupResult {
+ outputStream.write(data)
+ return backupResult
+ }
+
+ override fun restore(restoreContext: RestoreContext, inputStream: InputStream) {
+ restoredData = inputStream.readBytes()
+ inputStream.close()
+ }
+
+ fun verifyRestoredData() = assertThat(restoredData).isEqualTo(data)
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
index b52586c..8638b2f 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
@@ -16,76 +16,58 @@
package com.android.settingslib.datastore
+import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
-import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import com.google.common.util.concurrent.MoreExecutors
import java.util.concurrent.Executor
import java.util.concurrent.atomic.AtomicInteger
import org.junit.Assert
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.reset
import org.mockito.kotlin.verify
-import org.robolectric.RobolectricTestRunner
-@RunWith(RobolectricTestRunner::class)
+@RunWith(AndroidJUnit4::class)
class KeyedObserverTest {
- @get:Rule
- val mockitoRule: MockitoRule = MockitoJUnit.rule()
+ private val observer1 = mock<KeyedObserver<Any?>>()
+ private val observer2 = mock<KeyedObserver<Any?>>()
+ private val keyedObserver1 = mock<KeyedObserver<Any>>()
+ private val keyedObserver2 = mock<KeyedObserver<Any>>()
- @Mock
- private lateinit var observer1: KeyedObserver<Any?>
+ private val key1 = Object()
+ private val key2 = Object()
- @Mock
- private lateinit var observer2: KeyedObserver<Any?>
-
- @Mock
- private lateinit var keyedObserver1: KeyedObserver<Any>
-
- @Mock
- private lateinit var keyedObserver2: KeyedObserver<Any>
-
- @Mock
- private lateinit var key1: Any
-
- @Mock
- private lateinit var key2: Any
-
- @Mock
- private lateinit var executor: Executor
-
+ private val executor1: Executor = MoreExecutors.directExecutor()
+ private val executor2: Executor = MoreExecutors.newDirectExecutorService()
private val keyedObservable = KeyedDataObservable<Any>()
@Test
fun addObserver_sameExecutor() {
- keyedObservable.addObserver(observer1, executor)
- keyedObservable.addObserver(observer1, executor)
+ keyedObservable.addObserver(observer1, executor1)
+ keyedObservable.addObserver(observer1, executor1)
}
@Test
fun addObserver_keyedObserver_sameExecutor() {
- keyedObservable.addObserver(key1, keyedObserver1, executor)
- keyedObservable.addObserver(key1, keyedObserver1, executor)
+ keyedObservable.addObserver(key1, keyedObserver1, executor1)
+ keyedObservable.addObserver(key1, keyedObserver1, executor1)
}
@Test
fun addObserver_differentExecutor() {
- keyedObservable.addObserver(observer1, executor)
+ keyedObservable.addObserver(observer1, executor1)
Assert.assertThrows(IllegalStateException::class.java) {
- keyedObservable.addObserver(observer1, directExecutor())
+ keyedObservable.addObserver(observer1, executor2)
}
}
@Test
fun addObserver_keyedObserver_differentExecutor() {
- keyedObservable.addObserver(key1, keyedObserver1, executor)
+ keyedObservable.addObserver(key1, keyedObserver1, executor1)
Assert.assertThrows(IllegalStateException::class.java) {
- keyedObservable.addObserver(key1, keyedObserver1, directExecutor())
+ keyedObservable.addObserver(key1, keyedObserver1, executor2)
}
}
@@ -93,7 +75,7 @@
fun addObserver_weaklyReferenced() {
val counter = AtomicInteger()
var observer: KeyedObserver<Any?>? = KeyedObserver { _, _ -> counter.incrementAndGet() }
- keyedObservable.addObserver(observer!!, directExecutor())
+ keyedObservable.addObserver(observer!!, executor1)
keyedObservable.notifyChange(ChangeReason.UPDATE)
assertThat(counter.get()).isEqualTo(1)
@@ -111,7 +93,7 @@
fun addObserver_keyedObserver_weaklyReferenced() {
val counter = AtomicInteger()
var keyObserver: KeyedObserver<Any>? = KeyedObserver { _, _ -> counter.incrementAndGet() }
- keyedObservable.addObserver(key1, keyObserver!!, directExecutor())
+ keyedObservable.addObserver(key1, keyObserver!!, executor1)
keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
assertThat(counter.get()).isEqualTo(1)
@@ -127,45 +109,43 @@
@Test
fun addObserver_notifyObservers_removeObserver() {
- keyedObservable.addObserver(observer1, directExecutor())
- keyedObservable.addObserver(observer2, executor)
+ keyedObservable.addObserver(observer1, executor1)
+ keyedObservable.addObserver(observer2, executor2)
keyedObservable.notifyChange(ChangeReason.UPDATE)
verify(observer1).onKeyChanged(null, ChangeReason.UPDATE)
- verify(observer2, never()).onKeyChanged(any(), any())
- verify(executor).execute(any())
+ verify(observer2).onKeyChanged(null, ChangeReason.UPDATE)
- reset(observer1, executor)
+ reset(observer1, observer2)
keyedObservable.removeObserver(observer2)
keyedObservable.notifyChange(ChangeReason.DELETE)
verify(observer1).onKeyChanged(null, ChangeReason.DELETE)
- verify(executor, never()).execute(any())
+ verify(observer2, never()).onKeyChanged(null, ChangeReason.DELETE)
}
@Test
fun addObserver_keyedObserver_notifyObservers_removeObserver() {
- keyedObservable.addObserver(key1, keyedObserver1, directExecutor())
- keyedObservable.addObserver(key2, keyedObserver2, executor)
+ keyedObservable.addObserver(key1, keyedObserver1, executor1)
+ keyedObservable.addObserver(key2, keyedObserver2, executor2)
keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
verify(keyedObserver1).onKeyChanged(key1, ChangeReason.UPDATE)
- verify(keyedObserver2, never()).onKeyChanged(any(), any())
- verify(executor, never()).execute(any())
+ verify(keyedObserver2, never()).onKeyChanged(key2, ChangeReason.UPDATE)
- reset(keyedObserver1, executor)
- keyedObservable.removeObserver(key2, keyedObserver2)
+ reset(keyedObserver1, keyedObserver2)
+ keyedObservable.removeObserver(key1, keyedObserver1)
keyedObservable.notifyChange(key1, ChangeReason.DELETE)
- verify(keyedObserver1).onKeyChanged(key1, ChangeReason.DELETE)
- verify(executor, never()).execute(any())
+ verify(keyedObserver1, never()).onKeyChanged(key1, ChangeReason.DELETE)
+ verify(keyedObserver2, never()).onKeyChanged(key2, ChangeReason.DELETE)
}
@Test
fun notifyChange_addMoreTypeObservers_checkOnKeyChanged() {
- keyedObservable.addObserver(observer1, directExecutor())
- keyedObservable.addObserver(key1, keyedObserver1, directExecutor())
- keyedObservable.addObserver(key2, keyedObserver2, directExecutor())
+ keyedObservable.addObserver(observer1, executor1)
+ keyedObservable.addObserver(key1, keyedObserver1, executor1)
+ keyedObservable.addObserver(key2, keyedObserver2, executor1)
keyedObservable.notifyChange(ChangeReason.UPDATE)
verify(observer1).onKeyChanged(null, ChangeReason.UPDATE)
@@ -191,10 +171,10 @@
fun notifyChange_addObserverWithinCallback() {
// ConcurrentModificationException is raised if it is not implemented correctly
val observer: KeyedObserver<Any?> = KeyedObserver { _, _ ->
- keyedObservable.addObserver(observer1, executor)
+ keyedObservable.addObserver(observer1, executor1)
}
- keyedObservable.addObserver(observer, directExecutor())
+ keyedObservable.addObserver(observer, executor1)
keyedObservable.notifyChange(ChangeReason.UPDATE)
keyedObservable.removeObserver(observer)
@@ -204,12 +184,12 @@
fun notifyChange_KeyedObserver_addObserverWithinCallback() {
// ConcurrentModificationException is raised if it is not implemented correctly
val keyObserver: KeyedObserver<Any?> = KeyedObserver { _, _ ->
- keyedObservable.addObserver(key1, keyedObserver1, executor)
+ keyedObservable.addObserver(key1, keyedObserver1, executor1)
}
- keyedObservable.addObserver(key1, keyObserver, directExecutor())
+ keyedObservable.addObserver(key1, keyObserver, executor1)
keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
keyedObservable.removeObserver(key1, keyObserver)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
index f065829..173c2b1 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
@@ -22,40 +22,33 @@
import java.util.concurrent.Executor
import java.util.concurrent.atomic.AtomicInteger
import org.junit.Assert.assertThrows
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.reset
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class ObserverTest {
- @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+ private val observer1 = mock<Observer>()
+ private val observer2 = mock<Observer>()
- @Mock private lateinit var observer1: Observer
-
- @Mock private lateinit var observer2: Observer
-
- @Mock private lateinit var executor: Executor
-
+ private val executor1: Executor = MoreExecutors.directExecutor()
+ private val executor2: Executor = MoreExecutors.newDirectExecutorService()
private val observable = DataObservable()
@Test
fun addObserver_sameExecutor() {
- observable.addObserver(observer1, executor)
- observable.addObserver(observer1, executor)
+ observable.addObserver(observer1, executor1)
+ observable.addObserver(observer1, executor1)
}
@Test
fun addObserver_differentExecutor() {
- observable.addObserver(observer1, executor)
+ observable.addObserver(observer1, executor1)
assertThrows(IllegalStateException::class.java) {
- observable.addObserver(observer1, MoreExecutors.directExecutor())
+ observable.addObserver(observer1, executor2)
}
}
@@ -63,7 +56,7 @@
fun addObserver_weaklyReferenced() {
val counter = AtomicInteger()
var observer: Observer? = Observer { counter.incrementAndGet() }
- observable.addObserver(observer!!, MoreExecutors.directExecutor())
+ observable.addObserver(observer!!, executor1)
observable.notifyChange(ChangeReason.UPDATE)
assertThat(counter.get()).isEqualTo(1)
@@ -79,31 +72,27 @@
@Test
fun addObserver_notifyObservers_removeObserver() {
- observable.addObserver(observer1, MoreExecutors.directExecutor())
- observable.addObserver(observer2, executor)
+ observable.addObserver(observer1, executor1)
+ observable.addObserver(observer2, executor2)
observable.notifyChange(ChangeReason.DELETE)
verify(observer1).onChanged(ChangeReason.DELETE)
- verify(observer2, never()).onChanged(any())
- verify(executor).execute(any())
+ verify(observer2).onChanged(ChangeReason.DELETE)
- reset(observer1, executor)
+ reset(observer1, observer2)
observable.removeObserver(observer2)
observable.notifyChange(ChangeReason.UPDATE)
verify(observer1).onChanged(ChangeReason.UPDATE)
- verify(executor, never()).execute(any())
+ verify(observer2, never()).onChanged(ChangeReason.UPDATE)
}
@Test
fun notifyChange_addObserverWithinCallback() {
// ConcurrentModificationException is raised if it is not implemented correctly
- val observer = Observer { observable.addObserver(observer1, executor) }
- observable.addObserver(
- observer,
- MoreExecutors.directExecutor()
- )
+ val observer = Observer { observable.addObserver(observer1, executor1) }
+ observable.addObserver(observer, executor1)
observable.notifyChange(ChangeReason.UPDATE)
observable.removeObserver(observer)
}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt
new file mode 100644
index 0000000..fec7d75
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.app.Application
+import android.content.Context
+import android.content.SharedPreferences
+import android.os.Build
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.util.concurrent.Executor
+import kotlin.random.Random
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+
+/** Tests of [SharedPreferencesStorage]. */
+@RunWith(AndroidJUnit4::class)
+class SharedPreferencesStorageTest {
+ private val random = Random.Default
+ private val application: Application = ApplicationProvider.getApplicationContext()
+ private val map =
+ mapOf(
+ "boolean" to true,
+ "float" to random.nextFloat(),
+ "int" to random.nextInt(),
+ "long" to random.nextLong(),
+ "string" to "string",
+ "set" to setOf("string"),
+ )
+
+ @After
+ fun tearDown() {
+ application.getSharedPreferences(NAME, MODE).edit().clear().applySync()
+ }
+
+ @Test
+ fun constructors() {
+ val storage1 = SharedPreferencesStorage(application, NAME, MODE)
+ val storage2 =
+ SharedPreferencesStorage(
+ application,
+ NAME,
+ application.getSharedPreferences(NAME, MODE),
+ )
+ assertThat(storage1.sharedPreferences).isSameInstanceAs(storage2.sharedPreferences)
+ }
+
+ @Test
+ fun observer() {
+ val observer = mock<KeyedObserver<Any?>>()
+ val keyedObserver = mock<KeyedObserver<Any>>()
+ val storage = SharedPreferencesStorage(application, NAME, MODE)
+ val executor: Executor = MoreExecutors.directExecutor()
+ storage.addObserver(observer, executor)
+ storage.addObserver("key", keyedObserver, executor)
+
+ storage.sharedPreferences.edit().putString("key", "string").applySync()
+ verify(observer).onKeyChanged("key", ChangeReason.UPDATE)
+ verify(keyedObserver).onKeyChanged("key", ChangeReason.UPDATE)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ storage.sharedPreferences.edit().clear().applySync()
+ verify(observer).onKeyChanged(null, ChangeReason.DELETE)
+ verify(keyedObserver).onKeyChanged("key", ChangeReason.DELETE)
+ }
+ }
+
+ @Test
+ fun prepareBackup_commitFailed() {
+ val editor = mock<SharedPreferences.Editor> { on { commit() } doReturn false }
+ val storage =
+ spy(SharedPreferencesStorage(application, NAME, MODE)) {
+ onGeneric { mergeSharedPreferences(any(), any(), any()) } doReturn editor
+ }
+ storage.prepareBackup(File(""))
+ }
+
+ @Test
+ fun backupAndRestore() {
+ fun test(codec: BackupCodec) {
+ val storage = SharedPreferencesStorage(application, NAME, MODE, codec)
+ storage.mergeSharedPreferences(storage.sharedPreferences, map, "op").commit()
+ assertThat(storage.sharedPreferences.all).isEqualTo(map)
+
+ val outputStream = ByteArrayOutputStream()
+ assertThat(storage.toBackupRestoreEntity().backup(BackupContext(mock()), outputStream))
+ .isEqualTo(EntityBackupResult.UPDATE)
+ val payload = outputStream.toByteArray()
+
+ storage.sharedPreferences.edit().clear().commit()
+ assertThat(storage.sharedPreferences.all).isEmpty()
+
+ BackupRestoreFileArchiver(application, listOf(storage), "archiver")
+ .restoreEntity(newBackupDataInputStream(storage.storageFilePath, payload))
+ assertThat(storage.sharedPreferences.all).isEqualTo(map)
+ }
+
+ for (codec in allCodecs()) test(codec)
+ }
+
+ @Test
+ fun mergeSharedPreferences_filter() {
+ val storage =
+ SharedPreferencesStorage(application, NAME, MODE) { key, value ->
+ key == "float" || value is String
+ }
+ storage.mergeSharedPreferences(storage.sharedPreferences, map, "op").apply()
+ assertThat(storage.sharedPreferences.all)
+ .containsExactly("float", map["float"], "string", map["string"])
+ }
+
+ @Test
+ fun mergeSharedPreferences_invalidSet() {
+ val storage = SharedPreferencesStorage(application, NAME, MODE, verbose = true)
+ storage
+ .mergeSharedPreferences(
+ storage.sharedPreferences,
+ mapOf<String, Any>("set" to setOf(Any())),
+ "op"
+ )
+ .apply()
+ assertThat(storage.sharedPreferences.all).isEmpty()
+ }
+
+ @Test
+ fun mergeSharedPreferences_unknownType() {
+ val storage = SharedPreferencesStorage(application, NAME, MODE)
+ storage
+ .mergeSharedPreferences(storage.sharedPreferences, map + ("key" to Any()), "op")
+ .apply()
+ assertThat(storage.sharedPreferences.all).isEqualTo(map)
+ }
+
+ @Test
+ fun mergeSharedPreferences() {
+ val storage = SharedPreferencesStorage(application, NAME, MODE, verbose = true)
+ storage.mergeSharedPreferences(storage.sharedPreferences, map, "op").apply()
+ assertThat(storage.sharedPreferences.all).isEqualTo(map)
+ }
+
+ private fun SharedPreferences.Editor.applySync() {
+ apply()
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ }
+
+ companion object {
+ private const val NAME = "pref"
+ private const val MODE = Context.MODE_PRIVATE
+ }
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/TestUtils.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/TestUtils.kt
new file mode 100644
index 0000000..823d222
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/TestUtils.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.app.backup.BackupDataInput
+import android.app.backup.BackupDataInputStream
+import android.os.Build
+import java.io.ByteArrayInputStream
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.mock
+
+internal const val MAX_DATA_SIZE = 1 shl 12
+
+internal fun allCodecs() =
+ arrayOf<BackupCodec>(
+ BackupNoOpCodec(),
+ ) + zipCodecs()
+
+internal fun zipCodecs() =
+ arrayOf<BackupCodec>(
+ BackupZipCodec.DEFAULT_COMPRESSION,
+ BackupZipCodec.BEST_COMPRESSION,
+ BackupZipCodec.BEST_SPEED,
+ )
+
+internal fun <T : Any> Class<T>.newInstance(arg: Any, type: Class<*> = arg.javaClass): T =
+ getDeclaredConstructor(type).apply { isAccessible = true }.newInstance(arg)
+
+internal fun newBackupDataInputStream(
+ key: String,
+ data: ByteArray,
+ e: Exception? = null,
+): BackupDataInputStream {
+ // ShadowBackupDataOutput does not write data to file, so mock for reading data
+ val inputStream = ByteArrayInputStream(data)
+ val backupDataInput =
+ mock<BackupDataInput> {
+ on { readEntityData(any(), any(), any()) } doAnswer
+ {
+ if (e != null) throw e
+ val buf = it.arguments[0] as ByteArray
+ val offset = it.arguments[1] as Int
+ val size = it.arguments[2] as Int
+ inputStream.read(buf, offset, size)
+ }
+ }
+ return BackupDataInputStream::class
+ .java
+ .newInstance(backupDataInput, BackupDataInput::class.java)
+ .apply {
+ setKey(key)
+ setDataSize(data.size)
+ }
+}
+
+internal fun BackupDataInputStream.setKey(value: Any) {
+ val field = javaClass.getDeclaredField("key")
+ field.isAccessible = true
+ field.set(this, value)
+}
+
+internal fun BackupDataInputStream.setDataSize(dataSize: Int) {
+ val field = javaClass.getDeclaredField("dataSize")
+ field.isAccessible = true
+ field.setInt(this, dataSize)
+}
+
+internal fun isRobolectric() = Build.FINGERPRINT.contains("robolectric")
diff --git a/packages/SettingsLib/HelpUtils/res/values-pt-rPT/strings.xml b/packages/SettingsLib/HelpUtils/res/values-pt-rPT/strings.xml
index 9095f8e..f0bee5e 100644
--- a/packages/SettingsLib/HelpUtils/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values-pt-rPT/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="help_feedback_label" msgid="7106780063063027882">"Ajuda e comentários"</string>
+ <string name="help_feedback_label" msgid="7106780063063027882">"Ajuda e feedback"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
index fa5df16..b5534b9 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
@@ -18,5 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"एडमिन की ओर से चालू किया गया"</string>
- <string name="disabled_by_admin" msgid="4023569940620832713">"एडमिन की ओर से बंद किया गया"</string>
+ <string name="disabled_by_admin" msgid="4023569940620832713">"एडमिन ने यह सुविधा बंद की है"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-bn/strings.xml b/packages/SettingsLib/SearchWidget/res/values-bn/strings.xml
index 23f46bf..473231a 100644
--- a/packages/SettingsLib/SearchWidget/res/values-bn/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-bn/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1914043873178389845">"সেটিংসে সার্চ করুন"</string>
+ <string name="search_menu" msgid="1914043873178389845">"সার্চ সেটিংস"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-ms/strings.xml b/packages/SettingsLib/SearchWidget/res/values-ms/strings.xml
index 0d3a4da..99a9b05 100644
--- a/packages/SettingsLib/SearchWidget/res/values-ms/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-ms/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1914043873178389845">"Tetapan carian"</string>
+ <string name="search_menu" msgid="1914043873178389845">"Cari tetapan"</string>
</resources>
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 761bb79..91bd791 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -40,7 +40,7 @@
import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
import com.android.settingslib.spa.gallery.page.LoadingBarPageProvider
import com.android.settingslib.spa.gallery.page.ProgressBarPageProvider
-import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
+import com.android.settingslib.spa.gallery.scaffold.NonScrollablePagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.ListPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.MainSwitchPreferencePageProvider
@@ -48,10 +48,12 @@
import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
+import com.android.settingslib.spa.gallery.scaffold.PagerMainPageProvider
import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
import com.android.settingslib.spa.gallery.scaffold.SuwScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.CopyablePageProvider
+import com.android.settingslib.spa.gallery.scaffold.ScrollablePagerPageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.slice.SpaSliceBroadcastReceiver
@@ -84,7 +86,9 @@
ArgumentPageProvider,
SliderPageProvider,
SpinnerPageProvider,
- SettingsPagerPageProvider,
+ PagerMainPageProvider,
+ NonScrollablePagerPageProvider,
+ ScrollablePagerPageProvider,
FooterPageProvider,
IllustrationPageProvider,
CategoryPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index 1f028d5..654719d 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -39,9 +39,9 @@
import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
import com.android.settingslib.spa.gallery.page.LoadingBarPageProvider
import com.android.settingslib.spa.gallery.page.ProgressBarPageProvider
-import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
+import com.android.settingslib.spa.gallery.scaffold.PagerMainPageProvider
import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
import com.android.settingslib.spa.gallery.scaffold.SuwScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
@@ -63,7 +63,7 @@
SuwScaffoldPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SliderPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SpinnerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
- SettingsPagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ PagerMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
FooterPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
IllustrationPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
CategoryPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/NonScrollablePagerPageProvider.kt
similarity index 70%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/NonScrollablePagerPageProvider.kt
index dc45e6d..029773f 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/NonScrollablePagerPageProvider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.gallery.page
+package com.android.settingslib.spa.gallery.scaffold
import android.os.Bundle
import androidx.compose.foundation.layout.Box
@@ -33,24 +33,19 @@
import com.android.settingslib.spa.widget.scaffold.SettingsScaffold
import com.android.settingslib.spa.widget.ui.PlaceholderTitle
-private const val TITLE = "Sample SettingsPager"
+object NonScrollablePagerPageProvider : SettingsPageProvider {
+ override val name = "NonScrollablePager"
+ private const val TITLE = "Sample Non Scrollable SettingsPager"
-object SettingsPagerPageProvider : SettingsPageProvider {
- override val name = "SettingsPager"
+ fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = createSettingsPage())
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
- fun buildInjectEntry(): SettingsEntryBuilder {
- return SettingsEntryBuilder.createInject(owner = createSettingsPage())
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = TITLE
- override val onClick = navigator(name)
- })
- }
- }
-
- override fun getTitle(arguments: Bundle?): String {
- return TITLE
- }
+ override fun getTitle(arguments: Bundle?) = TITLE
@Composable
override fun Page(arguments: Bundle?) {
@@ -66,8 +61,8 @@
@Preview(showBackground = true)
@Composable
-private fun SettingsPagerPagePreview() {
+private fun NonScrollablePagerPageProviderPreview() {
SettingsTheme {
- SettingsPagerPageProvider.Page(null)
+ NonScrollablePagerPageProvider.Page(null)
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/PagerMainPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/PagerMainPageProvider.kt
new file mode 100644
index 0000000..66cc38f
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/PagerMainPageProvider.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.scaffold
+
+import android.os.Bundle
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+
+object PagerMainPageProvider : SettingsPageProvider {
+ override val name = "PagerMain"
+ private val owner = createSettingsPage()
+ private const val TITLE = "Category: Pager"
+
+ override fun buildEntry(arguments: Bundle?) = listOf(
+ NonScrollablePagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ ScrollablePagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ )
+
+ fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+
+ override fun getTitle(arguments: Bundle?) = TITLE
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/ScrollablePagerPageProvider.kt
similarity index 62%
copy from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
copy to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/ScrollablePagerPageProvider.kt
index dc45e6d..689a98a 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/ScrollablePagerPageProvider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.gallery.page
+package com.android.settingslib.spa.gallery.scaffold
import android.os.Bundle
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
@@ -31,33 +32,33 @@
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.SettingsPager
import com.android.settingslib.spa.widget.scaffold.SettingsScaffold
-import com.android.settingslib.spa.widget.ui.PlaceholderTitle
-private const val TITLE = "Sample SettingsPager"
+object ScrollablePagerPageProvider : SettingsPageProvider {
+ override val name = "ScrollablePager"
+ private const val TITLE = "Sample Scrollable SettingsPager"
-object SettingsPagerPageProvider : SettingsPageProvider {
- override val name = "SettingsPager"
+ fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = createSettingsPage())
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
- fun buildInjectEntry(): SettingsEntryBuilder {
- return SettingsEntryBuilder.createInject(owner = createSettingsPage())
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = TITLE
- override val onClick = navigator(name)
- })
- }
- }
-
- override fun getTitle(arguments: Bundle?): String {
- return TITLE
- }
+ override fun getTitle(arguments: Bundle?) = TITLE
@Composable
override fun Page(arguments: Bundle?) {
SettingsScaffold(title = getTitle(arguments)) { paddingValues ->
Box(Modifier.padding(paddingValues)) {
SettingsPager(listOf("Personal", "Work")) {
- PlaceholderTitle("Page $it")
+ LazyColumn {
+ items(30) {
+ Preference(object : PreferenceModel {
+ override val title = it.toString()
+ })
+ }
+ }
}
}
}
@@ -66,8 +67,8 @@
@Preview(showBackground = true)
@Composable
-private fun SettingsPagerPagePreview() {
+private fun ScrollablePagerPageProviderPreview() {
SettingsTheme {
- SettingsPagerPageProvider.Page(null)
+ ScrollablePagerPageProvider.Page(null)
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt
index 6fc8de3..a0ab2ce 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt
@@ -22,6 +22,10 @@
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SignalCellularAlt
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
@@ -37,6 +41,8 @@
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
import com.android.settingslib.spa.widget.ui.SettingsBody
+import com.android.settingslib.spa.widget.ui.Spinner
+import com.android.settingslib.spa.widget.ui.SpinnerOption
private const val TITLE = "Sample SuwScaffold"
@@ -67,13 +73,12 @@
actionButton = BottomAppBarButton("Next") {},
dismissButton = BottomAppBarButton("Cancel") {},
) {
- Column(Modifier.padding(SettingsDimension.itemPadding)) {
- SettingsBody("To add another SIM, download a new eSIM.")
- }
- Illustration(object : IllustrationModel {
- override val resId = R.drawable.accessibility_captioning_banner
- override val resourceType = ResourceType.IMAGE
- })
+ var selectedId by rememberSaveable { mutableIntStateOf(1) }
+ Spinner(
+ options = (1..3).map { SpinnerOption(id = it, text = "Option $it") },
+ selectedId = selectedId,
+ setId = { selectedId = it },
+ )
Column(Modifier.padding(SettingsDimension.itemPadding)) {
SettingsBody("To add another SIM, download a new eSIM.")
}
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 0ee9d59..85ad160 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
#
[versions]
-agp = "8.3.1"
+agp = "8.3.2"
compose-compiler = "1.5.11"
dexmaker-mockito = "2.28.3"
jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.6-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.6-bin.zip
deleted file mode 100644
index 5c96347..0000000
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.6-bin.zip
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.7-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.7-bin.zip
new file mode 100644
index 0000000..7a9ac5a
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.7-bin.zip
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 50ff9df..182095e 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,6 +16,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=gradle-8.6-bin.zip
+distributionUrl=gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 2f2ac24..6344501 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -65,7 +65,7 @@
api("androidx.lifecycle:lifecycle-runtime-compose")
api("androidx.navigation:navigation-compose:2.8.0-alpha05")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
- api("com.google.android.material:material:1.7.0-alpha03")
+ api("com.google.android.material:material:1.11.0")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
implementation("com.airbnb.android:lottie-compose:5.2.0")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
index bdc6a68..2284436 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
@@ -40,12 +40,13 @@
singleLine: Boolean = true,
enabled: Boolean = true,
shape: Shape = OutlinedTextFieldDefaults.shape,
+ modifier: Modifier = Modifier
+ .fillMaxWidth()
+ .padding(SettingsDimension.textFieldPadding),
onTextChange: (String) -> Unit
) {
OutlinedTextField(
- modifier = Modifier
- .fillMaxWidth()
- .padding(SettingsDimension.textFieldPadding),
+ modifier = modifier,
value = value,
onValueChange = onTextChange,
label = {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
index 791893b..ef1a137 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
@@ -37,6 +37,7 @@
SettingsSwitch(
checked = model.checked(),
changeable = model.changeable,
+ contentDescription = model.title,
onCheckedChange = model.onCheckedChange,
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
index f372a45..163766a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
@@ -19,7 +19,6 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
@@ -33,6 +32,8 @@
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.movableContentOf
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import com.android.settingslib.spa.framework.theme.SettingsDimension
@@ -50,7 +51,7 @@
title: String,
actionButton: BottomAppBarButton? = null,
dismissButton: BottomAppBarButton? = null,
- content: @Composable ColumnScope.() -> Unit,
+ content: @Composable () -> Unit,
) {
ActivityTitle(title)
Scaffold { innerPadding ->
@@ -59,6 +60,7 @@
.padding(innerPadding)
.padding(top = SettingsDimension.itemPaddingAround)
) {
+ val movableContent = remember(content) { movableContentOf { content() } }
// Use single column layout in portrait, two columns in landscape.
val useSingleColumn = maxWidth < maxHeight
if (useSingleColumn) {
@@ -69,7 +71,7 @@
.verticalScroll(rememberScrollState())
) {
Header(imageVector, title)
- content()
+ movableContent()
}
BottomBar(actionButton, dismissButton)
}
@@ -82,8 +84,9 @@
Column(
Modifier
.weight(1f)
- .verticalScroll(rememberScrollState())) {
- content()
+ .verticalScroll(rememberScrollState())
+ ) {
+ movableContent()
}
}
BottomBar(actionButton, dismissButton)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
index a0da241..5155406 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
@@ -20,12 +20,16 @@
import androidx.compose.material3.Switch
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog
@Composable
internal fun SettingsSwitch(
checked: Boolean?,
changeable: () -> Boolean,
+ contentDescription: String? = null,
onCheckedChange: ((newChecked: Boolean) -> Unit)? = null,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
@@ -33,6 +37,9 @@
Switch(
checked = checked,
onCheckedChange = wrapOnSwitchWithLog(onCheckedChange),
+ modifier = if (contentDescription != null) Modifier.semantics {
+ this.contentDescription = contentDescription
+ } else Modifier,
enabled = changeable(),
interactionSource = interactionSource,
)
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ar/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ar/strings.xml
index 9a8e7dd..f259541f 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ar/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ar/strings.xml
@@ -20,8 +20,8 @@
<string name="no_applications" msgid="5800789569715871963">"ليس هناك أي تطبيقات."</string>
<string name="menu_show_system" msgid="906304605807554788">"إظهار عمليات النظام"</string>
<string name="menu_hide_system" msgid="374571689914923020">"إخفاء عمليات النظام"</string>
- <string name="app_permission_summary_allowed" msgid="6115213465364138103">"مسموح به"</string>
- <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"غير مسموح به"</string>
+ <string name="app_permission_summary_allowed" msgid="6115213465364138103">"تطبيق مسموح به"</string>
+ <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"تطبيق غير مسموح به"</string>
<string name="version_text" msgid="4001669804596458577">"الإصدار <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
<string name="cloned_app_info_label" msgid="1765651167024478391">"نسخة طبق الأصل من \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\""</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-in/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-in/strings.xml
index a2a4289c..b1d5721 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-in/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-in/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_applications" msgid="5800789569715871963">"Tidak ada aplikasi."</string>
+ <string name="no_applications" msgid="5800789569715871963">"Tidak ada aplikasi"</string>
<string name="menu_show_system" msgid="906304605807554788">"Tampilkan sistem"</string>
<string name="menu_hide_system" msgid="374571689914923020">"Sembunyikan sistem"</string>
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Diizinkan"</string>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ne/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ne/strings.xml
index 7f3a523..87d0762 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ne/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ne/strings.xml
@@ -18,8 +18,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="no_applications" msgid="5800789569715871963">"कुनै पनि एप छैन।"</string>
- <string name="menu_show_system" msgid="906304605807554788">"सिस्टम देखाइयोस्"</string>
- <string name="menu_hide_system" msgid="374571689914923020">"सिस्टम लुकाइयोस्"</string>
+ <string name="menu_show_system" msgid="906304605807554788">"सिस्टम देखाउनुहोस्"</string>
+ <string name="menu_hide_system" msgid="374571689914923020">"सिस्टम लुकाउनुहोस्"</string>
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"अनुमति दिइएका एप"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"अनुमति नदिइएका एप"</string>
<string name="version_text" msgid="4001669804596458577">"संस्करण <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index a4089c0..7f4bebc 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -52,16 +52,12 @@
import java.util.Comparator;
import java.util.HashMap;
-/**
- * Description of a single dashboard tile that the user can select.
- */
+/** Description of a single dashboard tile that the user can select. */
public abstract class Tile implements Parcelable {
private static final String TAG = "Tile";
- /**
- * Optional list of user handles which the intent should be launched on.
- */
+ /** Optional list of user handles which the intent should be launched on. */
public ArrayList<UserHandle> userHandle = new ArrayList<>();
public HashMap<UserHandle, PendingIntent> pendingIntentMap = new HashMap<>();
@@ -76,6 +72,7 @@
private CharSequence mSummaryOverride;
private Bundle mMetaData;
private String mCategory;
+ private String mGroupKey;
public Tile(ComponentInfo info, String category, Bundle metaData) {
mComponentInfo = info;
@@ -83,6 +80,9 @@
mComponentName = mComponentInfo.name;
mCategory = category;
mMetaData = metaData;
+ if (mMetaData != null) {
+ mGroupKey = metaData.getString(META_DATA_PREFERENCE_GROUP_KEY);
+ }
mIntent = new Intent().setClassName(mComponentPackage, mComponentName);
if (isNewTask()) {
mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -102,6 +102,7 @@
if (isNewTask()) {
mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
+ mGroupKey = in.readString();
}
@Override
@@ -121,16 +122,13 @@
}
dest.writeString(mCategory);
dest.writeBundle(mMetaData);
+ dest.writeString(mGroupKey);
}
- /**
- * Unique ID of the tile
- */
+ /** Unique ID of the tile */
public abstract int getId();
- /**
- * Human-readable description of the tile
- */
+ /** Human-readable description of the tile */
public abstract String getDescription();
protected abstract ComponentInfo getComponentInfo(Context context);
@@ -147,16 +145,12 @@
return mComponentName;
}
- /**
- * Intent to launch when the preference is selected.
- */
+ /** Intent to launch when the preference is selected. */
public Intent getIntent() {
return mIntent;
}
- /**
- * Category in which the tile should be placed.
- */
+ /** Category in which the tile should be placed. */
public String getCategory() {
return mCategory;
}
@@ -165,9 +159,7 @@
mCategory = newCategoryKey;
}
- /**
- * Priority of this tile, used for display ordering.
- */
+ /** Priority of this tile, used for display ordering. */
public int getOrder() {
if (hasOrder()) {
return mMetaData.getInt(META_DATA_KEY_ORDER);
@@ -176,32 +168,24 @@
}
}
- /**
- * Check whether tile has order.
- */
+ /** Check whether tile has order. */
public boolean hasOrder() {
return mMetaData != null
&& mMetaData.containsKey(META_DATA_KEY_ORDER)
&& mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer;
}
- /**
- * Check whether tile has a switch.
- */
+ /** Check whether tile has a switch. */
public boolean hasSwitch() {
return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_SWITCH_URI);
}
- /**
- * Check whether tile has a pending intent.
- */
+ /** Check whether tile has a pending intent. */
public boolean hasPendingIntent() {
return !pendingIntentMap.isEmpty();
}
- /**
- * Title of the tile that is shown to the user.
- */
+ /** Title of the tile that is shown to the user. */
public CharSequence getTitle(Context context) {
CharSequence title = null;
ensureMetadataNotStale(context);
@@ -238,9 +222,7 @@
mSummaryOverride = summaryOverride;
}
- /**
- * Optional summary describing what this tile controls.
- */
+ /** Optional summary describing what this tile controls. */
public CharSequence getSummary(Context context) {
if (mSummaryOverride != null) {
return mSummaryOverride;
@@ -275,16 +257,12 @@
mMetaData = metaData;
}
- /**
- * The metaData from the activity that defines this tile.
- */
+ /** The metaData from the activity that defines this tile. */
public Bundle getMetaData() {
return mMetaData;
}
- /**
- * Optional key to use for this tile.
- */
+ /** Optional key to use for this tile. */
public String getKey(Context context) {
ensureMetadataNotStale(context);
if (!hasKey()) {
@@ -297,9 +275,7 @@
}
}
- /**
- * Check whether title has key.
- */
+ /** Check whether title has key. */
public boolean hasKey() {
return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_KEYHINT);
}
@@ -325,8 +301,9 @@
if (iconResId != 0 && iconResId != android.R.color.transparent) {
final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId);
if (isIconTintable(context)) {
- final TypedArray a = context.obtainStyledAttributes(new int[]{
- android.R.attr.colorControlNormal});
+ final TypedArray a =
+ context.obtainStyledAttributes(
+ new int[] {android.R.attr.colorControlNormal});
final int tintColor = a.getColor(0, 0);
a.recycle();
icon.setTint(tintColor);
@@ -349,26 +326,22 @@
return false;
}
- /**
- * Whether the {@link Activity} should be launched in a separate task.
- */
+ /** Whether the {@link Activity} should be launched in a separate task. */
public boolean isNewTask() {
- if (mMetaData != null
- && mMetaData.containsKey(META_DATA_NEW_TASK)) {
+ if (mMetaData != null && mMetaData.containsKey(META_DATA_NEW_TASK)) {
return mMetaData.getBoolean(META_DATA_NEW_TASK);
}
return false;
}
- /**
- * Ensures metadata is not stale for this tile.
- */
+ /** Ensures metadata is not stale for this tile. */
private void ensureMetadataNotStale(Context context) {
final PackageManager pm = context.getApplicationContext().getPackageManager();
try {
- final long lastUpdateTime = pm.getPackageInfo(mComponentPackage,
- PackageManager.GET_META_DATA).lastUpdateTime;
+ final long lastUpdateTime =
+ pm.getPackageInfo(mComponentPackage, PackageManager.GET_META_DATA)
+ .lastUpdateTime;
if (lastUpdateTime == mLastUpdateTime) {
// All good. Do nothing
return;
@@ -382,72 +355,60 @@
}
}
- public static final Creator<Tile> CREATOR = new Creator<Tile>() {
- public Tile createFromParcel(Parcel source) {
- final boolean isProviderTile = source.readBoolean();
- // reset the Parcel pointer before delegating to the real constructor.
- source.setDataPosition(0);
- return isProviderTile ? new ProviderTile(source) : new ActivityTile(source);
- }
+ public static final Creator<Tile> CREATOR =
+ new Creator<Tile>() {
+ public Tile createFromParcel(Parcel source) {
+ final boolean isProviderTile = source.readBoolean();
+ // reset the Parcel pointer before delegating to the real constructor.
+ source.setDataPosition(0);
+ return isProviderTile ? new ProviderTile(source) : new ActivityTile(source);
+ }
- public Tile[] newArray(int size) {
- return new Tile[size];
- }
- };
+ public Tile[] newArray(int size) {
+ return new Tile[size];
+ }
+ };
- /**
- * Check whether tile only has primary profile.
- */
+ /** Check whether tile only has primary profile. */
public boolean isPrimaryProfileOnly() {
return isPrimaryProfileOnly(mMetaData);
}
static boolean isPrimaryProfileOnly(Bundle metaData) {
- String profile = metaData != null
- ? metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+ String profile = metaData != null ? metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
profile = (profile != null ? profile : PROFILE_ALL);
return TextUtils.equals(profile, PROFILE_PRIMARY);
}
- /**
- * Returns whether the tile belongs to another group / category.
- */
+ /** Returns whether the tile belongs to another group / category. */
public boolean hasGroupKey() {
- return mMetaData != null
- && !TextUtils.isEmpty(mMetaData.getString(META_DATA_PREFERENCE_GROUP_KEY));
+ return !TextUtils.isEmpty(mGroupKey);
}
- /**
- * Returns the group / category key this tile belongs to.
- */
+ /** Set the group / PreferenceCategory key this tile belongs to. */
+ public void setGroupKey(String groupKey) {
+ mGroupKey = groupKey;
+ }
+
+ /** Returns the group / category key this tile belongs to. */
public String getGroupKey() {
- return (mMetaData == null) ? null : mMetaData.getString(META_DATA_PREFERENCE_GROUP_KEY);
+ return mGroupKey;
}
- /**
- * Returns if this is searchable.
- */
+ /** Returns if this is searchable. */
public boolean isSearchable() {
return mMetaData == null || mMetaData.getBoolean(META_DATA_PREFERENCE_SEARCHABLE, true);
}
- /**
- * The type of the tile.
- */
+ /** The type of the tile. */
public enum Type {
- /**
- * A preference that can be tapped on to open a new page.
- */
+ /** A preference that can be tapped on to open a new page. */
ACTION,
- /**
- * A preference that can be tapped on to open an external app.
- */
+ /** A preference that can be tapped on to open an external app. */
EXTERNAL_ACTION,
- /**
- * A preference that shows an on / off switch that can be toggled by the user.
- */
+ /** A preference that shows an on / off switch that can be toggled by the user. */
SWITCH,
/**
@@ -460,7 +421,7 @@
* A preference category with a title that can be used to group multiple preferences
* together.
*/
- GROUP;
+ GROUP
}
/**
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index d0929e1..b949cd5 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -77,9 +77,7 @@
*/
public static final String IA_SETTINGS_ACTION = "com.android.settings.action.IA_SETTINGS";
- /**
- * Same as #EXTRA_SETTINGS_ACTION but used for the platform Settings activities.
- */
+ /** Same as #EXTRA_SETTINGS_ACTION but used for the platform Settings activities. */
private static final String SETTINGS_ACTION = "com.android.settings.action.SETTINGS";
private static final String OPERATOR_SETTINGS =
@@ -101,9 +99,7 @@
*/
static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
- /**
- * The key used to get the package name of the icon resource for the preference.
- */
+ /** The key used to get the package name of the icon resource for the preference. */
static final String EXTRA_PREFERENCE_ICON_PACKAGE = "com.android.settings.icon_package";
/**
@@ -145,18 +141,17 @@
"com.android.settings.bg.argb";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify the content provider providing the icon that should be displayed for
- * the preference.
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
+ * content provider providing the icon that should be displayed for the preference.
*
- * Icon provided by the content provider overrides any static icon.
+ * <p>Icon provided by the content provider overrides any static icon.
*/
public static final String META_DATA_PREFERENCE_ICON_URI = "com.android.settings.icon_uri";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify whether the icon is tintable. This should be a boolean value {@code true} or
- * {@code false}, set using {@code android:value}
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify whether
+ * the icon is tintable. This should be a boolean value {@code true} or {@code false}, set using
+ * {@code android:value}
*/
public static final String META_DATA_PREFERENCE_ICON_TINTABLE =
"com.android.settings.icon_tintable";
@@ -171,41 +166,36 @@
public static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify the content provider providing the title text that should be displayed for the
- * preference.
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
+ * content provider providing the title text that should be displayed for the preference.
*
- * Title provided by the content provider overrides any static title.
+ * <p>Title provided by the content provider overrides any static title.
*/
- public static final String META_DATA_PREFERENCE_TITLE_URI =
- "com.android.settings.title_uri";
+ public static final String META_DATA_PREFERENCE_TITLE_URI = "com.android.settings.title_uri";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify the summary text that should be displayed for the preference.
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
+ * summary text that should be displayed for the preference.
*/
public static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify the content provider providing the summary text that should be displayed for the
- * preference.
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
+ * content provider providing the summary text that should be displayed for the preference.
*
- * Summary provided by the content provider overrides any static summary.
+ * <p>Summary provided by the content provider overrides any static summary.
*/
public static final String META_DATA_PREFERENCE_SUMMARY_URI =
"com.android.settings.summary_uri";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify the content provider providing the switch that should be displayed for the
- * preference.
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
+ * content provider providing the switch that should be displayed for the preference.
*
- * This works with {@link #META_DATA_PREFERENCE_KEYHINT} which should also be set in the
+ * <p>This works with {@link #META_DATA_PREFERENCE_KEYHINT} which should also be set in the
* AndroidManifest.xml
*/
- public static final String META_DATA_PREFERENCE_SWITCH_URI =
- "com.android.settings.switch_uri";
+ public static final String META_DATA_PREFERENCE_SWITCH_URI = "com.android.settings.switch_uri";
/**
* Name of the meta-data item that can be set from the content provider providing the intent
@@ -215,8 +205,8 @@
"com.android.settings.pending_intent";
/**
- * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile,
- * the app will always be run in the primary profile.
+ * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile, the app will
+ * always be run in the primary profile.
*
* @see #META_DATA_KEY_PROFILE
*/
@@ -231,11 +221,11 @@
public static final String PROFILE_ALL = "all_profiles";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify the profile in which the app should be run when the device has a managed profile.
- * The default value is {@link #PROFILE_ALL} which means the user will be presented with a
- * dialog to choose the profile. If set to {@link #PROFILE_PRIMARY} the app will always be
- * run in the primary profile.
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
+ * profile in which the app should be run when the device has a managed profile. The default
+ * value is {@link #PROFILE_ALL} which means the user will be presented with a dialog to choose
+ * the profile. If set to {@link #PROFILE_PRIMARY} the app will always be run in the primary
+ * profile.
*
* @see #PROFILE_PRIMARY
* @see #PROFILE_ALL
@@ -243,20 +233,16 @@
public static final String META_DATA_KEY_PROFILE = "com.android.settings.profile";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify whether the {@link android.app.Activity} should be launched in a separate task.
- * This should be a boolean value {@code true} or {@code false}, set using {@code android:value}
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify whether
+ * the {@link android.app.Activity} should be launched in a separate task. This should be a
+ * boolean value {@code true} or {@code false}, set using {@code android:value}
*/
public static final String META_DATA_NEW_TASK = "com.android.settings.new_task";
- /**
- * If the entry should be shown in settings search results. Defaults to true.
- */
+ /** If the entry should be shown in settings search results. Defaults to true. */
public static final String META_DATA_PREFERENCE_SEARCHABLE = "com.android.settings.searchable";
- /**
- * Build a list of DashboardCategory.
- */
+ /** Build a list of DashboardCategory. */
public static List<DashboardCategory> getCategories(Context context,
Map<Pair<String, String>, Tile> cache) {
final long startTime = System.currentTimeMillis();
@@ -341,8 +327,8 @@
UserHandle user, Map<Pair<String, String>, Tile> addedCache,
String defaultCategory, List<Tile> outTiles, Intent intent) {
final PackageManager pm = context.getPackageManager();
- final List<ResolveInfo> results = pm.queryIntentContentProvidersAsUser(intent,
- 0 /* flags */, user.getIdentifier());
+ final List<ResolveInfo> results =
+ pm.queryIntentContentProvidersAsUser(intent, 0 /* flags */, user.getIdentifier());
for (ResolveInfo resolved : results) {
if (!resolved.system) {
// Do not allow any app to add to settings, only system ones.
@@ -403,6 +389,8 @@
tile.setMetaData(metaData);
}
+ tile.setGroupKey(metaData.getString(META_DATA_PREFERENCE_GROUP_KEY));
+
if (!tile.userHandle.contains(user)) {
tile.userHandle.add(user);
}
@@ -448,15 +436,15 @@
/**
* Returns the complete uri from the meta data key of the tile.
*
- * A complete uri should contain at least one path segment and be one of the following types:
- * content://authority/method
- * content://authority/method/key
+ * <p>A complete uri should contain at least one path segment and be one of the following types:
+ * <br>content://authority/method
+ * <br>content://authority/method/key
*
- * If the uri from the tile is not complete, build a uri by the default method and the
+ * <p>If the uri from the tile is not complete, build a uri by the default method and the
* preference key.
*
- * @param tile Tile which contains meta data
- * @param metaDataKey Key mapping to the uri in meta data
+ * @param tile Tile which contains meta data
+ * @param metaDataKey Key mapping to the uri in meta data
* @param defaultMethod Method to be attached to the uri by default if it has no path segment
* @return Uri associated with the key
*/
@@ -501,9 +489,9 @@
/**
* Gets the icon package name and resource id from content provider.
*
- * @param context context
+ * @param context context
* @param packageName package name of the target activity
- * @param uri URI for the content provider
+ * @param uri URI for the content provider
* @param providerMap Maps URI authorities to providers
* @return package name and resource id of the icon specified
*/
@@ -532,10 +520,10 @@
/**
* Gets text associated with the input key from the content provider.
*
- * @param context context
- * @param uri URI for the content provider
+ * @param context context
+ * @param uri URI for the content provider
* @param providerMap Maps URI authorities to providers
- * @param key Key mapping to the text in bundle returned by the content provider
+ * @param key Key mapping to the text in bundle returned by the content provider
* @return Text associated with the key, if returned by the content provider
*/
public static String getTextFromUri(Context context, Uri uri,
@@ -547,10 +535,10 @@
/**
* Gets boolean associated with the input key from the content provider.
*
- * @param context context
- * @param uri URI for the content provider
+ * @param context context
+ * @param uri URI for the content provider
* @param providerMap Maps URI authorities to providers
- * @param key Key mapping to the text in bundle returned by the content provider
+ * @param key Key mapping to the text in bundle returned by the content provider
* @return Boolean associated with the key, if returned by the content provider
*/
public static boolean getBooleanFromUri(Context context, Uri uri,
@@ -562,11 +550,11 @@
/**
* Puts boolean associated with the input key to the content provider.
*
- * @param context context
- * @param uri URI for the content provider
+ * @param context context
+ * @param uri URI for the content provider
* @param providerMap Maps URI authorities to providers
- * @param key Key mapping to the text in bundle returned by the content provider
- * @param value Boolean associated with the key
+ * @param key Key mapping to the text in bundle returned by the content provider
+ * @param value Boolean associated with the key
* @return Bundle associated with the action, if returned by the content provider
*/
public static Bundle putBooleanToUriAndGetResult(Context context, Uri uri,
diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
index 712f6f0..ea3dbd9 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
+++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
@@ -20,6 +20,7 @@
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
import android.text.style.AbsoluteSizeSpan;
import android.util.AttributeSet;
import android.view.View;
@@ -174,6 +175,7 @@
bottomSummary.setVisibility(View.GONE);
} else {
bottomSummary.setVisibility(View.VISIBLE);
+ bottomSummary.setMovementMethod(LinkMovementMethod.getInstance());
bottomSummary.setText(mBottomSummary);
}
diff --git a/packages/SettingsLib/aconfig/OWNERS b/packages/SettingsLib/aconfig/OWNERS
new file mode 100644
index 0000000..ba02d20
--- /dev/null
+++ b/packages/SettingsLib/aconfig/OWNERS
@@ -0,0 +1,2 @@
+# go/android-fwk-media-solutions for info on areas of ownership.
+per-file settingslib_media_flag_declarations.aconfig = file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index e09ab00..89f54d9 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -36,14 +36,14 @@
name: "enable_le_audio_sharing"
namespace: "pixel_cross_device_control"
description: "Gates whether to enable LE audio sharing"
- bug: "305620450"
+ bug: "323125723"
}
flag {
name: "enable_le_audio_qr_code_private_broadcast_sharing"
namespace: "pixel_cross_device_control"
description: "Gates whether to enable LE audio private broadcast sharing via QR code"
- bug: "308368124"
+ bug: "323125723"
}
flag {
@@ -52,3 +52,13 @@
description: "Hide exclusively managed Bluetooth devices in BT settings menu."
bug: "324475542"
}
+
+flag {
+ name: "enable_set_preferred_transport_for_le_audio_device"
+ namespace: "bluetooth"
+ description: "Enable setting preferred transport for Le Audio device"
+ bug: "330581926"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
index 4d70aec..7aae1a6 100644
--- a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
@@ -21,3 +21,13 @@
description: "Enable Output Switcher when no media is playing."
bug: "284227163"
}
+
+flag {
+ name: "remove_unnecessary_route_scanning"
+ namespace: "media_solutions"
+ description: "Avoid active scan requests on UI components that only display route status information."
+ bug: "332515672"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
index 89d6ac3..3ed1724 100644
--- a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
+++ b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
@@ -53,7 +53,7 @@
<EditText
android:id="@+id/user_name"
android:layout_width="match_parent"
- android:layout_height="@dimen/user_name_height_in_user_info_dialog"
+ android:layout_height="wrap_content"
android:layout_gravity="center"
android:minWidth="200dp"
android:layout_marginStart="6dp"
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index eb3d4af..e4be79b 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktief, net links"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktief, net regs"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktief, links en regs"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktiewe (net media), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>-battery"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktiewe (net media), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>-battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>-battery"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Gekoppelde (steun oudiodeling), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>-battery"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Gekoppelde (steun oudiodeling), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>-battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>-battery"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Gekoppelde (steun oudiodeling), linkerkantse <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Gekoppelde (steun oudiodeling), regterkantse <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktief (net media)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Steun oudiodeling"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktief (net media), net linkerkant"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktief (net media), net regterkant"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktief (net media), linker- en regterkant"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Media-oudio"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Foonoproepe"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Lêeroordrag"</string>
@@ -335,8 +324,8 @@
<string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Wanneer hierdie modus geaktiveer is, kan hierdie toestel se MAC-adres verander elke keer wanneer dit aan \'n netwerk koppel waarvoor MAC-verewekansiging geaktiveer is."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Beperk"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Onbeperk"</string>
- <string name="select_logd_size_title" msgid="1604578195914595173">"Loggerbuffer se groottes"</string>
- <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Kies loggergroottes per logbuffer"</string>
+ <string name="select_logd_size_title" msgid="1604578195914595173">"Logskepperbuffer se groottes"</string>
+ <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Kies logskeppergroottes per logbuffer"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"Maak logskrywer se aanhoudende berging skoon?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"Wanneer ons nie meer monitering met die aanhoudende logskrywer uitvoer nie, word ons vereis om die logskrywerdata wat op jou toestel is, uit te vee."</string>
<string name="select_logpersist_title" msgid="447071974007104196">"Berg logskrywerdata aanhoudend op toestel"</string>
@@ -411,7 +400,7 @@
<string name="show_non_rect_clip" msgid="7499758654867881817">"Ontfout nie-reghoekige knipbedrywighede"</string>
<string name="track_frame_time" msgid="522674651937771106">"Profiel-HWUI-lewering"</string>
<string name="enable_gpu_debug_layers" msgid="4986675516188740397">"Aktiveer GPU-ontfoutlae"</string>
- <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Laat laai van GPU-ontfoutlae vir ontfoutprogramme toe"</string>
+ <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Laat laai van GPU-ontfoutlae vir ontfoutapps toe"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Aktiveer woordryke verkoperloginskrywing"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Sluit bykomende toestelspesifieke verkoperloglêers by foutverslae in, wat privaat inligting kan bevat, meer batterykrag kan gebruik, en/of meer berging kan gebruik."</string>
<string name="window_animation_scale_title" msgid="5236381298376812508">"Vensteranimasieskaal"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> oor tot vol"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laaiproses word geoptimeer"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laai tans"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laai"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laai tans vinnig"</string>
@@ -509,12 +506,16 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Gelaai"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Volgelaai"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Laai wag tans"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Beheer deur administrateur"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Beheer deur Beperkte Instellings"</string>
<string name="disabled" msgid="8017887509554714950">"Gedeaktiveer"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Toegelaat"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Nie toegelaat nie"</string>
- <string name="install_other_apps" msgid="3232595082023199454">"Installeer onbekende programme"</string>
+ <string name="install_other_apps" msgid="3232595082023199454">"Installeer onbekende apps"</string>
<string name="home" msgid="973834627243661438">"Instellingstuisblad"</string>
<string-array name="battery_labels">
<item msgid="7878690469765357158">"0%"</item>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Foon, een staaf."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Foon, twee stawe."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Foon, drie stawe."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Foonsein is vol."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Geen data nie."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data, een staaf."</string>
@@ -709,7 +712,7 @@
<string name="keyboard_layout_default_label" msgid="1997292217218546957">"Verstek"</string>
<string name="turn_screen_on_title" msgid="3266937298097573424">"Skakel skerm aan"</string>
<string name="allow_turn_screen_on" msgid="6194845766392742639">"Laat toe dat die skerm aangeskakel word"</string>
- <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Laat ’n program toe om die skerm aan te skakel. As jy toestemming gee, kan die program die skerm enige tyd sonder jou uitdruklike bedoeling aanskakel."</string>
+ <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Laat ’n app toe om die skerm aan te skakel. As jy toestemming gee, kan die app die skerm enige tyd sonder jou uitdruklike bedoeling aanskakel."</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Hou op om <xliff:g id="APP_NAME">%1$s</xliff:g> uit te saai?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"As jy <xliff:g id="SWITCHAPP">%1$s</xliff:g> uitsaai of die uitvoer verander, sal jou huidige uitsending stop"</string>
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Saai <xliff:g id="SWITCHAPP">%1$s</xliff:g> uit"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index bda0277..368698c 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"ገቢር፣ ግራ ብቻ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"ገቢር፣ ቀኝ ብቻ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"ገቢር፣ ግራ እና ቀኝ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"ገቢር (ሚዲያ ብቻ), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ባትሪ"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"ገቢር (ሚዲያ ብቻ), ግ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ባትሪ፣ ቀ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ባትሪ"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"የተገናኘ (የድምፅ ማጋራት ይደግፋል), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ባትሪ"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"የተገናኘ (የድምፅ ማጋራት ይደግፋል) ግ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ባትሪ፣ ቀ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ባትሪ"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"የተገናኘ (የድምፅ ማጋራት ይደግፋል)፣ ግራ<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"የተገናኘ (የድምፅ ማጋራት ይደግፋል)፣ ቀኝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"ገቢር (ሚዲያ ብቻ)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ድምፅ ማጋራትን ይደግፋል"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"ገቢር (ሚዲያ ብቻ)፣ ግራ ብቻ"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"ገቢር (ሚዲያ ብቻ) ቀኝ ብቻ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"ገቢር (ሚዲያ ብቻ)፣ ግራ እና ቀኝ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"የማህደረ መረጃ ኦዲዮ"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"የስልክ ጥሪዎች"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ፋይል ማስተላለፍ"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስኪሞላ ድረስ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል መሙላት እንዲተባ ተደርጓል"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል በመሙላት ላይ"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ያልታወቀ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ኃይል በመሙላት ላይ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"ባትሪ ሞልቷል"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"ሙሉ ለሙሉ ኃይል ተሞልቷል"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ኃይል መሙላት በይቆይ ላይ"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"በአስተዳዳሪ ቁጥጥር የተደረገበት"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"በተገደበ ቅንብር ቁጥጥር የሚደረግበት"</string>
<string name="disabled" msgid="8017887509554714950">"ቦዝኗል"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"የስልክ አንድ አሞሌ"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"የስልክ ሁለት አሞሌ"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"የስልክ ሦስት አሞሌ"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"የስልክ አመልካች ሙሉ ነው።"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"ምንም ውሂብ የለም።"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"የውሂብ አንድ አሞሌ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index ba93f65..41b51a5 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -106,32 +106,21 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"السمّاعة الطبية اليسرى فقط مفعَّلة"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"السمّاعة الطبية اليمنى فقط مفعَّلة"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"السمّاعتان اليسرى واليمنى مفعَّلتان"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"البلوتوث مفعَّل (للوسائط فقط)، مستوى شحن البطارية: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"البلوتوث مفعَّل (للوسائط فقط)، مستوى الشحن في السماعة اليسرى: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، مستوى الشحن في السماعة اليمنى: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"البلوتوث متصل (ميزة \"مشاركة الصوت\" متاحة)، مستوى شحن البطارية: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"البلوتوث متصل (ميزة \"مشاركة الصوت\" متاحة)، مستوى الشحن في السماعة اليسرى: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، مستوى الشحن في السماعة اليمنى: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"البلوتوث متصل (ميزة \"مشاركة الصوت\" متاحة)، مستوى الشحن في السماعة اليسرى: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"البلوتوث متصل (ميزة \"مشاركة الصوت\" متاحة)، مستوى الشحن في السماعة اليمنى: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"البلوتوث مفعَّل (للوسائط فقط)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"تتوفّر ميزة \"مشاركة الصوت\""</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"السماعة اليسرى فقط مشغَّلة (للوسائط فقط)"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"السماعة اليمنى فقط مشغَّلة (للوسائط فقط)"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"السماعتان اليسرى واليمنى مشغَّلتان (للوسائط فقط)"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"الإعدادات الصوتية للوسائط"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"المكالمات الهاتفية"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"نقل الملف"</string>
- <string name="bluetooth_profile_hid" msgid="2969922922664315866">"جهاز الإرسال"</string>
+ <string name="bluetooth_profile_hid" msgid="2969922922664315866">"جهاز إدخال بيانات"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"الوصول إلى الإنترنت"</string>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"السماح بالوصول إلى جهات الاتصال وسجلّ المكالمات"</string>
<string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"سيتم استخدام المعلومات لإرسال إشعارات المكالمات وغيرها"</string>
@@ -202,8 +191,8 @@
<string name="launch_defaults_some" msgid="3631650616557252926">"تم ضبط بعض الإعدادات التلقائية"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"لم يتم ضبط إعدادات تلقائية"</string>
<string name="tts_settings" msgid="8130616705989351312">"إعدادات تحويل النص إلى كلام"</string>
- <string name="tts_settings_title" msgid="7602210956640483039">"إخراج النص إلى كلام"</string>
- <string name="tts_default_rate_title" msgid="3964187817364304022">"معدل سرعة الكلام"</string>
+ <string name="tts_settings_title" msgid="7602210956640483039">"الصوت عند تحويل النص إلى كلام"</string>
+ <string name="tts_default_rate_title" msgid="3964187817364304022">"سرعة الكلام"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"سرعة قول الكلام"</string>
<string name="tts_default_pitch_title" msgid="6988592215554485479">"درجة الصوت"</string>
<string name="tts_default_pitch_summary" msgid="9132719475281551884">"للتأثير في نبرة الكلام المُرَكَّب"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - يتبقّى <xliff:g id="TIME">%2$s</xliff:g> حتى اكتمال شحن البطارية."</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - تم تحسين الشحن"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g>: جارٍ الشحن"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"غير معروف"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"جارٍ الشحن"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string>
@@ -509,11 +506,15 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"مشحونة"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"البطارية مشحونة بالكامل."</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"الشحن معلَّق"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"إعدادات يتحكم فيها المشرف"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"يتحكّم فيه إعداد محظور"</string>
<string name="disabled" msgid="8017887509554714950">"غير مفعّل"</string>
- <string name="external_source_trusted" msgid="1146522036773132905">"مسموح به"</string>
- <string name="external_source_untrusted" msgid="5037891688911672227">"غير مسموح به"</string>
+ <string name="external_source_trusted" msgid="1146522036773132905">"تطبيق مسموح به"</string>
+ <string name="external_source_untrusted" msgid="5037891688911672227">"تطبيق غير مسموح به"</string>
<string name="install_other_apps" msgid="3232595082023199454">"تثبيت التطبيقات غير المعروفة"</string>
<string name="home" msgid="973834627243661438">"الشاشة الرئيسية للإعدادات"</string>
<string-array name="battery_labels">
@@ -555,7 +556,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"المنبّهات والتذكيرات"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"السماح بضبط المنبّهات والتذكيرات"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"المنبّهات والتذكيرات"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"يمكنك السماح لهذا التطبيق بضبط المنبّهات وجدولة الإجراءات لتنفيذها في الوقت المناسب. ويسمح هذا الإذن بتشغيل التطبيق في الخلفية، ما قد يستهلك المزيد من البطارية.\n\nفي حال عدم تفعيل هذا الإذن، لن تعمل المنبهات الحالية والأحداث المستندة إلى الوقت المضبوطة في هذا التطبيق."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"يمكنك السماح لهذا التطبيق بضبط المنبّهات وجدولة الإجراءات لتنفيذها في الوقت المناسب. ويسمح هذا الإذن بتشغيل التطبيق في الخلفية، ما قد يستهلك المزيد من البطارية.\n\nفي حال عدم تفعيل هذا الإذن، لن تعمل المنبهات المضبوطة والأحداث المستندة إلى الوقت المجدولة حاليًا في هذا التطبيق."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"جدول زمني، جدولة، منبّه، تذكير، ساعة"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"تفعيل"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"تفعيل ميزة \"عدم الإزعاج\""</string>
@@ -668,7 +669,7 @@
<string name="user_image_photo_selector" msgid="433658323306627093">"اختيار صورة"</string>
<string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"لقد استنفدت عدد المحاولات غير الصحيحة وسيتم حذف بيانات هذا الجهاز."</string>
<string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"لقد استنفدت عدد المحاولات غير الصحيحة وسيتم حذف حساب هذا المستخدم."</string>
- <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"لقد استنفدت عدد المحاولات غير الصحيحة وسيتم حذف الملف الشخصي للعمل وبياناته."</string>
+ <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"لقد استنفدت عدد المحاولات غير الصحيحة وسيتم حذف ملف العمل وبياناته."</string>
<string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"إغلاق"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"الإعداد التلقائي للجهاز"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غير مفعّل"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"إشارة الهاتف تتكون من شريط واحد."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"إشارة الهاتف تتكون من شريطين."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"إشارة الهاتف تتكون من ثلاثة أشرطة."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"إشارة الهاتف كاملة."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"لا تتوفر بيانات."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"إشارة البيانات تتكون من شريط واحد."</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index ef53faf..b5ea37f 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"কেৱল বাঁওফালৰটো সক্ৰিয় হৈছে"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"কেৱল সোঁফালৰটো সক্ৰিয় হৈছে"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"বাওঁ আৰু সোঁ দুয়োফালৰ সক্ৰিয় হৈছে"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"সক্ৰিয় হৈ আছে (কেৱল মিডিয়া), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বেটাৰী"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"সক্ৰিয় হৈ আছে (কেৱল মিডিয়া), বাওঁ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> বেটাৰী, সোঁ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> বেটাৰী"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"সংযুক্ত হৈ আছে (অডিঅ’ শ্বেয়াৰিং সমৰ্থন কৰে), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বেটাৰী"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"সংযুক্ত হৈ আছে (অডিঅ’ শ্বেয়াৰিং সমৰ্থন কৰে), বাওঁ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> বেটাৰী, সোঁ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> বেটাৰী"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"সংযুক্ত হৈ আছে (অডিঅ’ শ্বেয়াৰিং সমৰ্থন কৰে), বাওঁ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"সংযুক্ত হৈ আছে (অডিঅ’ শ্বেয়াৰিং সমৰ্থন কৰে), সোঁ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"সক্ৰিয় হৈ আছে (কেৱল মিডিয়া)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"অডিঅ’ শ্বেয়াৰিং সমৰ্থন কৰে"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"সক্ৰিয় হৈ আছে (কেৱল মিডিয়া), কেৱল বাওঁ"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"সক্ৰিয় হৈ আছে (কেৱল মিডিয়া), কেৱল সোঁ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"সক্ৰিয় হৈ আছে (কেৱল মিডিয়া), বাওঁ আৰু সোঁ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"মিডিয়াৰ অডিঅ’"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ফ\'ন কলসমূহ"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ফাইল স্থানান্তৰণ"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - চাৰ্জিং অপ্টিমাইজ কৰা হৈছে"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ চাৰ্জ হৈ আছে"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"অজ্ঞাত"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"চাৰ্জ কৰি থকা হৈছে"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্ৰুততাৰে চাৰ্জ হৈছে"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"চাৰ্জ হ’ল"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"সম্পূৰ্ণ চাৰ্জ হৈছে"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"চাৰ্জিং স্থগিত ৰখা হৈছে"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"এডমিনৰ দ্বাৰা নিয়ন্ত্ৰিত"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"প্ৰতিবন্ধিত ছেটিঙৰ দ্বাৰা নিয়ন্ত্ৰিত"</string>
<string name="disabled" msgid="8017887509554714950">"নিষ্ক্ৰিয়"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"ফ\'ন ছিগনেলৰ এডাল দণ্ড।"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"ফ\'ন ছিগনেলৰ দুডাল দণ্ড।"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"ফ\'নৰ ছিগনেলৰ তিনিডাল দণ্ড আছে।"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"ফ\'নৰ ছিগনেল পূৰা আছে৷"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"কোনো ডেটা নাই।"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"ডেটা ছিগনেলৰ এডাল দণ্ড।"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 48c68f4..9324252 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiv, yalnız sol"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiv, yalnız sağ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiv, sol və sağ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktiv (yalnız media), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batareya"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktiv (yalnız media), Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batareya, Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batareya"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Qoşulub (audio paylaşma dəstəklənir), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batareya"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Qoşulub (audio paylaşma dəstəklənir), Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batareya, Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batareya"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Qoşulub (audio paylaşma dəstəklənir), sol <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Qoşulub (audio paylaşma dəstəklənir), sağ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktiv (yalnız media)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Audio paylaşma dəstəklənir"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktiv (yalnız media), yalnız sol"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktiv (yalnız media), yalnız sağ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktiv (yalnız media), sol və sağ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Media audio"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefon zəngləri"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fayl transferi"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - tam şarj edilənədək <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj optimallaşdırılıb"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj edilir"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Naməlum"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Enerji doldurma"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Sürətlə doldurulur"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Şarj edilib"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Tam şarj edilib"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Şarj gözlədilir"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Admin tərəfindən nəzarət olunur"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Məhdudlaşdırılmış Ayar ilə nəzarət edilir"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktiv"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Şəbəkə bir xətdir."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Şəbəkə iki xətdir."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Şəbəkə üç xətdir."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Tam şəbəkə."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Məlumat yoxdur."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data bir xətdir."</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 04c6d94..02e5f69 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktivno, samo s leve strane"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktivno, s desne strane"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktivno, s leve i desne strane"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktivan (samo za medije), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktivan (samo za medije), levo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> baterije, desno: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterije"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Povezan (podržava deljenje zvuka), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Povezan (podržava deljenje zvuka), levo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> baterije, desno: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterije"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Povezan (podržava deljenje zvuka), levo <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Povezan (podržava deljenje zvuka), desno <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktivan (samo za medije)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Podržava deljenje zvuka"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktivan (samo za medije), samo levo"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktivan (samo za medije), samo desno"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktivan (samo za medije), levo i desno"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Zvuk medija"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonski pozivi"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos datoteke"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do kraja punjenja"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizovano"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Punjenje"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Puni se"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Napunjeno do kraja"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Punjenje je na čekanju"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontroliše administrator"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolišu ograničena podešavanja"</string>
<string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Signal telefona ima jednu crtu."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Signal telefona od dve crte."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Signal telefona od tri crte."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Signal telefona je pun."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Nema podataka."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Signal za podatke ima jednu crtu."</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index f8c88e6..2658891 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Уключана, толькі для левага вуха"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Уключана, толькі для правага вуха"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Уключана, для левага і правага вуха"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Выкарыстоўваецца (толькі для мультымедыя), зарад акумулятара: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Выкарыстоўваецца (толькі для мультымедыя), зарад акумулятара: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (левы навушнік), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (правы навушнік)"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Падключана (падтрымліваецца абагульванне аўдыя), зарад акумулятара: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Падключана (падтрымліваецца абагульванне аўдыя), зарад акумулятара: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (левы навушнік), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (правы навушнік)"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Падключана (падтрымліваецца абагульванне аўдыя), зарад акумулятара: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (левы навушнік)"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Падключана (падтрымліваецца абагульванне аўдыя), зарад акумулятара: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (правы навушнік)"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Выкарыстоўваецца (толькі для мультымедыя)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Падтрымліваецца абагульванне аўдыя"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Выкарыстоўваецца (толькі для мультымедыя), толькі левы навушнік"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Выкарыстоўваецца (толькі для мультымедыя), толькі правы навушнік"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Выкарыстоўваецца (толькі для мультымедыя), левы і правы навушнікі"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Аўдыя медыяфайлаў"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Тэлефонныя выклікі"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Перадача файлаў"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – да поўнай зарадкі засталося: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зарадка аптымізавана"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – зараджаецца"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Невядома"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарадка"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хуткая зарадка"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Зараджаны"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Акумулятар поўнасцю зараджаны"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Зарадка прыпынена"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Кантралюецца адміністратарам"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Пад кіраваннем Абмежаванага наладжвання"</string>
<string name="disabled" msgid="8017887509554714950">"Адключанае"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Адна планка на тэлефоне."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"2 планкі тэлефона."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"3 планкі тэлефона."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Поўны сігнал тэлефона."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Няма дадзеных."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Адна планка дадзеных."</string>
diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml
index 9388e66..31d24c1 100644
--- a/packages/SettingsLib/res/values-bg/arrays.xml
+++ b/packages/SettingsLib/res/values-bg/arrays.xml
@@ -188,7 +188,7 @@
<item msgid="409235464399258501">"Изключено"</item>
<item msgid="4195153527464162486">"Рег. буфер – 64 КБ"</item>
<item msgid="7464037639415220106">"Рег. буфер – 256 КБ"</item>
- <item msgid="8539423820514360724">"Рег. буфер – 1 МБ"</item>
+ <item msgid="8539423820514360724">"Рег. буфер – 1 млн."</item>
<item msgid="1984761927103140651">"Рег. буфер – 4 МБ"</item>
<item msgid="2983219471251787208">"Регистрационен буфер – 8 МБ"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 0457f10..85eaccd 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -106,35 +106,24 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Активно – само лявото"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Активно – само дясното"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Активно – лявото и дясното"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Активно (само за мултимедия), батерия – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Активно (само за мултимедия), Л: батерия – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Д: батерия – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Свързано (поддържа споделяне на звука), батерия – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Свързано (поддържа споделяне на звука), Л: батерия – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Д: батерия – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Свързано (поддържа споделяне на звука), лява – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Свързано (поддържа споделяне на звука), дясна – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Активно (само за мултимедия)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Поддържа споделяне на звука"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Активно (само за мултимедия), само лявата"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Активно (само за мултимедия), само дясната"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Активно (само за мултимедия), лявата и дясната"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Мултимедийно аудио"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Телефонни обаждания"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Прехвърляне на файл"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Входно устройство"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Достъп до интернет"</string>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Разреш. на достъпа до контактите и историята на обажд."</string>
- <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Информацията ще се ползва за съобщения чрез обаждания и др."</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>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оставащо време до пълно зареждане: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зареждането е оптимизирано"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарежда се"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарежда се"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Заредена"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Напълно заредено"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Зареждането е поставено на пауза"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Контролира се от администратор"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Управлява се чрез ограничена настройка"</string>
<string name="disabled" msgid="8017887509554714950">"Деактивирано"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Телефонът е с една чертичка."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Телефонът е с две чертички."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Телефонът е с три чертички."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Сигналът за телефона е пълен."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Няма данни."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Данните са с една чертичка."</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index e9367ff..d4485b8 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"শুধুমাত্র বাঁদিকের হিয়ারিং এড অ্যাক্টিভ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"শুধুমাত্র ডানদিকের হিয়ারিং এড অ্যাক্টিভ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"বাঁ ও ডানদিকের হিয়ারিং এড, অ্যাক্টিভ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"চালু আছে (শুধুমাত্র মিডিয়া), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ব্যাটারি"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"চালু আছে (শুধুমাত্র মিডিয়া), বাঁদিক: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ব্যাটারি, ডানদিক: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ব্যাটারি"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"কানেক্ট করা আছে (অডিও শেয়ারিংয়ে কাজ করে), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ব্যাটারি"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"কানেক্ট করা আছে (অডিও শেয়ারিংয়ে কাজ করে), বাঁদিক: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ব্যাটারি, ডানদিক: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ব্যাটারি"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"কানেক্ট করা আছে (অডিও শেয়ারিংয়ে কাজ করে), বাঁদিক <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"কানেক্ট করা আছে (অডিও শেয়ারিংয়ে কাজ করে), ডানদিক <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"চালু আছে (শুধুমাত্র মিডিয়া)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"অডিও শেয়ারিংয়ে কাজ করে"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"চালু আছে (শুধুমাত্র মিডিয়া), শুধুমাত্র বাঁদিক"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"চালু আছে (শুধুমাত্র মিডিয়া), শুধুমাত্র ডানদিক"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"চালু আছে (শুধুমাত্র মিডিয়া), বাঁদিক ও ডানদিক"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"মিডিয়া অডিও"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ফোন কল"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ফাইল স্থানান্তর"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এ ব্যাটারি পুরো চার্জ হয়ে যাবে"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জিং অপ্টিমাইজ করা হয়েছে"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জ করা হচ্ছে"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"অজানা"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"চার্জ হচ্ছে"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্রুত চার্জ হচ্ছে"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"চার্জ হয়েছে"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"সম্পূর্ণ চার্জ আছে"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"চার্জিং হোল্ডে আছে"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"প্রশাসকের দ্বারা নিয়ন্ত্রিত"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"এটি বিধিনিষেধ সেটিং থেকে নিয়ন্ত্রণ করা হয়"</string>
<string name="disabled" msgid="8017887509554714950">"অক্ষম হয়েছে"</string>
@@ -537,7 +538,7 @@
<string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"সিস্টেমের ভাষাগুলি ব্যবহার করুন"</string>
<string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> জন্য সেটিংস খুলতে ব্যর্থ হয়েছে"</string>
<string name="ime_security_warning" msgid="6547562217880551450">"এই ইনপুট পদ্ধতিটি হয়ত পাসওয়ার্ড এবং ক্রেডিট কার্ড নম্বর সহ আপনার টাইপ করা সমস্ত টেক্সট সংগ্রহ করতে সক্ষম হতে পারে। এটি <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> অ্যাপ থেকে এসেছে। এই ইনপুট পদ্ধতিটি ব্যবহার করবেন?"</string>
- <string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"দ্রষ্টব্য: পুনরায় চালু করার পরে, আপনি আপনার ফোন আনলক না করা পর্যন্ত এই অ্যাপটিকে চালু করতে পারবেন না"</string>
+ <string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"দ্রষ্টব্য: রিবুট করার পরে, আপনি আপনার ফোন আনলক না করা পর্যন্ত এই অ্যাপটিকে চালু করতে পারবেন না"</string>
<string name="ims_reg_title" msgid="8197592958123671062">"IMS রেজিস্ট্রেশনের স্থিতি"</string>
<string name="ims_reg_status_registered" msgid="884916398194885457">"রেজিস্টার করা"</string>
<string name="ims_reg_status_not_registered" msgid="2989287366045704694">"রেজিস্টার করা নয়"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"এক দন্ড ফোনের সংকেত রয়েছে৷"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"দুই দন্ড ফোনের সংকেত রয়েছে৷"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"তিন দন্ড ফোনের সংকেত রয়েছে৷"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"ফোনের সংকেত পূর্ণ রয়েছে৷"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"কোনো ডেটা নেই৷"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"এক দন্ড ডেটার সংকেত৷"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 7fb5225..4f9c1f8 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktivno, samo lijevi"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktivno, samo desni"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktivno, lijevi i desni"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktivno (samo za medijski sadržaj), baterija: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktivno (samo za medijski sadržaj), baterija L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, baterija D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Povezano (podržava dijeljenje zvuka), baterija: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Povezano (podržava dijeljenje zvuka), baterija L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, baterija D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Povezano (podržava dijeljenje zvuka), lijevo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Povezano (podržava dijeljenje zvuka), desno: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktivno (samo za medijski sadržaj)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Podržava dijeljenje zvuka"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktivno (samo za medijski sadržaj), samo lijevo"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktivno (samo za medijski sadržaj), samo desno"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktivno (samo za medijski sadržaj), lijevo i desno"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Zvuk medija"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonski pozivi"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenošenje fajla"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do potpune napunjenosti"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizirano"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Potpuno napunjeno"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Punjenje je na čekanju"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Pod kontrolom administratora"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolira ograničena postavka"</string>
<string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefonski signal na jednoj crtici."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefonski signal na dvije crtice."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefonski signal na tri crtice."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefonski signal pun."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Nema podataka."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Prijenos podataka na jednoj crtici."</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 18da1be..8a6c26c 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Actiu, només l\'esquerre"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Actiu, només el dret"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Actiu, esquerre i dret"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Actiu (només contingut multimèdia), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Actiu (només contingut multimèdia), E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Connectat (admet compartició d\'àudio), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Connectat (admet compartició d\'àudio), E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Connectat (admet compartició d\'àudio), esquerre <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Connectat (admet compartició d\'àudio), dret <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Actiu (només contingut multimèdia)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Admet compartició d\'àudio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Actiu (només contingut multimèdia), només esquerre"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Actiu (només contingut multimèdia), només dret"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Actiu (només contingut multimèdia), esquerre i dret"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Àudio multimèdia"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Trucades telefòniques"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferència de fitxers"</string>
@@ -487,7 +476,7 @@
<string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"Hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> segons l\'ús que en facis"</string>
<string name="power_discharge_by" msgid="4113180890060388350">"Hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"Hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="5883041507426914446">"fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"És possible que la bateria s\'esgoti a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g>: càrrega optimitzada"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g>: s\'està carregant"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconegut"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"S\'està carregant"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Càrrega ràpida"</string>
@@ -509,12 +506,16 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Totalment carregada"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Càrrega en espera"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlat per l\'administrador"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlat per l\'opció de configuració restringida"</string>
<string name="disabled" msgid="8017887509554714950">"Desactivat"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Amb permís"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Sense permís"</string>
- <string name="install_other_apps" msgid="3232595082023199454">"Instal·lar aplicacions desconegudes"</string>
+ <string name="install_other_apps" msgid="3232595082023199454">"Instal·la aplicacions desconegudes"</string>
<string name="home" msgid="973834627243661438">"Pàgina d\'inici de configuració"</string>
<string-array name="battery_labels">
<item msgid="7878690469765357158">"0%"</item>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Senyal de telèfon: una barra"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Senyal de telèfon: dues barres."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Senyal de telèfon: tres barres."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Senyal de telèfon: complet."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Senyal de dades: no n\'hi ha"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Senyal de dades: una barra."</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index f4bfe505c..dc6d2d0 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktivní, pouze levé"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktivní, pouze pravé"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktivní, levé a pravé"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktivní (pouze média), baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktivní (pouze média), baterie: L <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, P <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Připojeno (podporuje sdílení zvuku), baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Připojeno (podporuje sdílení zvuku), baterie: L <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, P <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Připojeno (podporuje sdílení zvuku), levé <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Připojeno (podporuje sdílení zvuku), pravé <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktivní (pouze média)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Podporuje sdílení zvuku"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktivní (pouze média), pouze levé"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktivní (pouze média), pouze pravé"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktivní (pouze média), levé a pravé"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Zvuk médií"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonní hovory"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Přenos souborů"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – optimalizované nabíjení"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Nabíjení"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Neznámé"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíjí se"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rychlé nabíjení"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Nabito"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Plně nabito"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Nabíjení pozastaveno"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Spravováno administrátorem"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Spravováno omezeným nastavením"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktivováno"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Jedna čárka signálu telefonní sítě."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Dvě čárky signálu telefonní sítě."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Tři čárky signálu telefonní sítě."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Plný signál telefonní sítě."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Žádné datové připojení."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Jedna čárka signálu datové sítě."</string>
@@ -709,7 +712,7 @@
<string name="keyboard_layout_default_label" msgid="1997292217218546957">"Výchozí"</string>
<string name="turn_screen_on_title" msgid="3266937298097573424">"Zapínání obrazovky"</string>
<string name="allow_turn_screen_on" msgid="6194845766392742639">"Povolit zapínání obrazovky"</string>
- <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Povolte aplikaci zapínat obrazovku. Pokud aplikace bude mít toto oprávnění, může kdykoli zapnout obrazovku bez požadavku uživatele."</string>
+ <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Aplikaci můžete povolit zapínat obrazovku. Pokud bude mít toto oprávnění, může kdykoli zapnout obrazovku bez požadavku uživatele."</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Zastavit vysílání v aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Pokud budete vysílat v aplikaci <xliff:g id="SWITCHAPP">%1$s</xliff:g> nebo změníte výstup, aktuální vysílání se zastaví"</string>
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Vysílat v aplikaci <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index d51f8b9..b4fee14 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiv, kun venstre"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiv, kun højre"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiv, venstre og højre"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktiveret (kun for medier), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktiveret (kun for medier), V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Forbundet (understøtter lyddeling), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Forbundet (understøtter lyddeling), V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Forbundet (understøtter lyddeling), venstre <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Forbundet (understøtter lyddeling), højre <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktiveret (kun for medier)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Understøtter lyddeling"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktiveret (kun for medier), kun venstre"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktiveret (kun for medier), kun højre"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktiveret (kun for medier), venstre og højre"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Medielyd"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonopkald"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filoverførsel"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – fuldt opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladning er optimeret"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – oplades"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ukendt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Oplader"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Oplader hurtigt"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Opladet"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fuldt opladet"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Opladningen er blevet sat på pause"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolleret af administratoren"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Styres af en begrænset indstilling"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktiveret"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefon en bjælke."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefon to bjælker."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefon tre bjælker."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefonsignal fuldt."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Ingen data."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data en bjælke."</string>
@@ -709,7 +712,7 @@
<string name="keyboard_layout_default_label" msgid="1997292217218546957">"Standard"</string>
<string name="turn_screen_on_title" msgid="3266937298097573424">"Aktivér skærmen"</string>
<string name="allow_turn_screen_on" msgid="6194845766392742639">"Tillad aktivering af skærmen"</string>
- <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Tillad, at en app aktiverer skærmen. Hvis du giver denne tilladelse, kan appen til enhver tid aktiverer skærmen, uden at du eksplicit har bedt om det."</string>
+ <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Tillad, at en app aktiverer skærmen. Hvis du giver denne tilladelse, kan appen til enhver tid aktivere skærmen, uden at du eksplicit har bedt om det."</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Stop udsendelsen <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Hvis du udsender <xliff:g id="SWITCHAPP">%1$s</xliff:g> eller skifter output, stopper din aktuelle udsendelse"</string>
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Udsend <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 08ca14a..91d108d 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiv, nur links"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiv, nur rechts"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiv, links und rechts"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktiv (nur Medien), Akku: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktiv (nur Medien), Akku links: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Akku rechts: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Verbunden (unterstützt Audiofreigabe), Akku: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Verbunden (unterstützt Audiofreigabe), Akku links: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Akku rechts: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Verbunden (unterstützt Audiofreigabe), Akku links: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Verbunden (unterstützt Audiofreigabe), Akku rechts: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktiv (nur Medien)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Unterstützt Audiofreigabe"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktiv (nur Medien), nur links"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktiv (nur Medien), nur rechts"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktiv (nur Medien), links und rechts"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Medien-Audio"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonanrufe"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dateiübertragung"</string>
@@ -472,7 +461,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (Rot-Grün-Sehschwäche)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (Blau-Gelb-Sehschwäche)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Farbkorrektur"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Die Farbkorrektur kann nützlich sein, wenn du:<br/> <ol> <li>&nbsp;Farben noch genauer sehen möchtest</li> <li>&nbsp;bestimmte Farben entfernen möchtest, um dich besser zu konzentrieren</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Die Farbkorrektur kann nützlich sein, wenn du:<br/> <ol> <li>&nbsp;Farben noch genauer sehen möchtest</li> <li>&nbsp;Bestimmte Farben entfernen möchtest, um dich besser zu konzentrieren</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladevorgang zum Schutz des Akkus angehalten"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laden wird optimiert"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Wird geladen"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unbekannt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Wird aufgeladen"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Schnelles Aufladen"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Aufgeladen"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Vollständig geladen"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Ladevorgang angehalten"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Durch den Administrator verwaltet"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Gesteuert durch eingeschränkte Einstellung"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktiviert"</string>
@@ -593,7 +594,7 @@
<string name="tv_media_transfer_default" msgid="5403053145185843843">"Standardeinstellung: Fernseher"</string>
<string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-Ausgang"</string>
<string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interne Lautsprecher"</string>
- <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Verbindung kann nicht hergestellt werden. Schalte das Gerät aus & und wieder ein."</string>
+ <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Verbindung kann nicht hergestellt werden. Schalte das Gerät aus und wieder ein."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Netzbetriebenes Audiogerät"</string>
<string name="help_label" msgid="3528360748637781274">"Hilfe und Feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Speicher"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefonsignal - ein Balken"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefonsignal - zwei Balken"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefonsignal - drei Balken"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Volle Telefonsignalstärke"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Keine Daten"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Datensignal - ein Balken"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index c38034a..e74e5c3 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Ενεργό, μόνο το αριστερό"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Ενεργό, μόνο το δεξί"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Ενεργό, αριστερό και δεξί"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Ενεργό (μόνο για μέσα), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> μπαταρία"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Ενεργό (μόνο για μέσα), Α: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> μπαταρία, Δ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> μπαταρία"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Συνδεδεμένο (υποστηρίζει κοινή χρήση ήχου), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> μπαταρία"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Συνδεδεμένο (υποστηρίζει κοινή χρήση ήχου), Α: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> μπαταρία, Δ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> μπαταρία"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Συνδεδεμένο (υποστηρίζει κοινή χρήση ήχου), αριστερό <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Συνδεδεμένο (υποστηρίζει κοινή χρήση ήχου), δεξί <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Ενεργό (μόνο για μέσα)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Υποστηρίζει κοινή χρήση ήχου"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Ενεργό (μόνο για μέσα), μόνο αριστερό"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Ενεργό (μόνο για μέσα), μόνο δεξί"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Ενεργό (μόνο για μέσα), αριστερό και δεξί"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Ήχος πολυμέσων"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Τηλεφωνικές κλήσεις"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Μεταφορά αρχείου"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Απομένουν <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Η φόρτιση βελτιστοποιήθηκε"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Φόρτιση"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Άγνωστο"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Φόρτιση"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Φορτισμένη"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Πλήρως φορτισμένο"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Η φόρτιση τέθηκε σε αναμονή"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Ελέγχονται από το διαχειριστή"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ελέγχεται από τη Ρύθμιση με περιορισμό"</string>
<string name="disabled" msgid="8017887509554714950">"Απενεργοποιημένο"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Μία γραμμή τηλεφώνου."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Δύο γραμμές τηλεφώνου."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Τρεις γραμμές μπαταρίας."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Πλήρες σήμα τηλεφώνου."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Δεν υπάρχουν δεδομένα."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Μία γραμμή δεδομένων."</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index c787c63..4b4154a 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Active, left only"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Active, right only"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Active, left and right"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Active (media only), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Active (media only), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Connected (supports audio sharing), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Connected (supports audio sharing), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Connected (supports audio sharing), left <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Connected (supports audio sharing), right <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Active (media only)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Supports audio sharing"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Active (media only), left only"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Active (media only), right only"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Active (media only), left and right"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Media audio"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Phone calls"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fully charged"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Charging on hold"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Phone one bar."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Phone two bars."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Phone three bars."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Phone signal full."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"No data."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data one bar."</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index e5d2e29..6a799a8 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -487,6 +487,10 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimized"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging"</string>
+ <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Full by <xliff:g id="TIME">%3$s</xliff:g>"</string>
+ <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fully charged by <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Fully charged by <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Full by <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
@@ -498,6 +502,8 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fully Charged"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Charging on hold"</string>
+ <string name="battery_info_status_charging_v2" msgid="6118522107222245505">"Charging"</string>
+ <string name="battery_info_status_charging_fast_v2" msgid="1825439848151256589">"Fast charging"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by Restricted Setting"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
@@ -684,6 +690,7 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Phone one bar."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Phone two bars."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Phone three bars."</string>
+ <string name="accessibility_phone_four_bars" msgid="4477202400261338403">"Phone four bars."</string>
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Phone signal full."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"No data."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data one bar."</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index c787c63..4b4154a 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Active, left only"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Active, right only"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Active, left and right"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Active (media only), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Active (media only), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Connected (supports audio sharing), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Connected (supports audio sharing), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Connected (supports audio sharing), left <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Connected (supports audio sharing), right <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Active (media only)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Supports audio sharing"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Active (media only), left only"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Active (media only), right only"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Active (media only), left and right"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Media audio"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Phone calls"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fully charged"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Charging on hold"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Phone one bar."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Phone two bars."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Phone three bars."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Phone signal full."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"No data."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data one bar."</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index c787c63..4b4154a 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Active, left only"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Active, right only"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Active, left and right"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Active (media only), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Active (media only), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Connected (supports audio sharing), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Connected (supports audio sharing), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> battery, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Connected (supports audio sharing), left <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Connected (supports audio sharing), right <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Active (media only)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Supports audio sharing"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Active (media only), left only"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Active (media only), right only"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Active (media only), left and right"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Media audio"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Phone calls"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fully charged"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Charging on hold"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Phone one bar."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Phone two bars."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Phone three bars."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Phone signal full."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"No data."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data one bar."</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 5e14648..7638057 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -487,6 +487,10 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimized"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging"</string>
+ <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Full by <xliff:g id="TIME">%3$s</xliff:g>"</string>
+ <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fully charged by <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Fully charged by <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Full by <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
@@ -498,6 +502,8 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fully Charged"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Charging on hold"</string>
+ <string name="battery_info_status_charging_v2" msgid="6118522107222245505">"Charging"</string>
+ <string name="battery_info_status_charging_fast_v2" msgid="1825439848151256589">"Fast charging"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by Restricted Setting"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
@@ -684,6 +690,7 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Phone one bar."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Phone two bars."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Phone three bars."</string>
+ <string name="accessibility_phone_four_bars" msgid="4477202400261338403">"Phone four bars."</string>
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Phone signal full."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"No data."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data one bar."</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml
index be12572..4d4d7c9 100644
--- a/packages/SettingsLib/res/values-es-rUS/arrays.xml
+++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml
@@ -186,10 +186,10 @@
</string-array>
<string-array name="select_logd_size_summaries">
<item msgid="409235464399258501">"Desactivado"</item>
- <item msgid="4195153527464162486">"64 K/búfer registro"</item>
- <item msgid="7464037639415220106">"256 K/búfer registro"</item>
- <item msgid="8539423820514360724">"1 M/búfer registro"</item>
- <item msgid="1984761927103140651">"4 M/búfer registro"</item>
+ <item msgid="4195153527464162486">"64 K/búfer de registro"</item>
+ <item msgid="7464037639415220106">"256 K/búfer de registro"</item>
+ <item msgid="8539423820514360724">"1 M/búfer de registro"</item>
+ <item msgid="1984761927103140651">"4 M/búfer de registro"</item>
<item msgid="2983219471251787208">"8 M/búfer de registro"</item>
</string-array>
<string-array name="select_logpersist_titles">
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 1903c6b..bad913c 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Activo; solo oído izquierdo"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Activo; solo oído derecho"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Activo; oídos izquierdo y derecho"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Activo (solo para contenido multimedia); <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Activo (solo para contenido multimedia); I: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería; D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Conectado (admite el uso compartido de audio); <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Conectado (admite el uso compartido de audio); I: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería; D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Conectado (admite el uso compartido de audio); izquierdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Conectado (admite el uso compartido de audio); derecho: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Activo (solo para contenido multimedia)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Admite el uso compartido de audio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Activo (solo para contenido multimedia); solo izquierdo"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Activo (solo para contenido multimedia); solo derecho"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Activo (solo para contenido multimedia); izquierdo y derecho"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Audio multimedia"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Llamadas telefónicas"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string>
@@ -335,7 +324,7 @@
<string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Si este modo está habilitado, es posible que la dirección MAC del dispositivo cambie cada vez que se conecte a una red que tenga habilitada la aleatorización de MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"De uso medido"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Sin tarifa plana"</string>
- <string name="select_logd_size_title" msgid="1604578195914595173">"Tamaños de búfer de Logger"</string>
+ <string name="select_logd_size_title" msgid="1604578195914595173">"Tamaños de los búferes de registro"</string>
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Selecciona el tamaño del Logger por búfer"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"¿Borrar el almacenamiento persistente del registrador?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"Cuando ya no usamos el registrador persistente para monitorear, debemos borrar los datos que almacena el registrador en tu dispositivo."</string>
@@ -411,7 +400,7 @@
<string name="show_non_rect_clip" msgid="7499758654867881817">"Depurar operaciones de recorte no rectangulares"</string>
<string name="track_frame_time" msgid="522674651937771106">"Perfil procesamiento HWUI"</string>
<string name="enable_gpu_debug_layers" msgid="4986675516188740397">"Habilitar depuración GPU"</string>
- <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permite capas de GPU para apps de depuración"</string>
+ <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permite cargar capas de depuración de GPU para apps de depuración"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Habilitar registro detallado"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Incluye otros registros de proveedor específicos del dispositivo en los informes de errores, que podrían contener información privada, consumir más batería o usar más espacio de almacenamiento."</string>
<string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animación de ventana"</string>
@@ -471,7 +460,7 @@
<string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"Deuteronomalía (rojo-verde)"</string>
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalía (rojo-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalía (azul-amarillo)"</string>
- <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección de color"</string>
+ <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección de colores"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"La corrección de colores puede ser útil cuando quieres hacer lo siguiente:<br/> <ol> <li>&nbsp;Ver los colores con mayor precisión</li> <li>&nbsp;Quitar colores para concentrarte</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Reemplazado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Cargando"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Carga completa"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Se detuvo la carga"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada por el administrador"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Función controlada por configuración restringida"</string>
<string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Una barra de teléfono"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Dos barras de teléfono"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Tres barras de teléfono"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Señal de teléfono completa"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"No hay datos."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Una barra de datos"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 29b29b6..b4627d2 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -106,35 +106,24 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Activo, solo oído izquierdo"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Activo, solo oído derecho"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Activo, oídos izquierdo y derecho"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Activo (solo multimedia), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Activo (solo multimedia), izquierdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería, derecho: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Conectado (permite compartir audio), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Conectado (permite compartir audio), izquierdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería, derecho: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Conectado (permite compartir audio), izquierdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Conectado (permite compartir audio), derecho: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Activo (solo multimedia)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Permite compartir audio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Activo (solo multimedia), solo el izquierdo"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Activo (solo multimedia), solo el derecho"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Activo (solo multimedia), izquierdo y derecho"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Audio multimedia"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Llamadas de teléfono"</string>
<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>
<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_pbap_summary" msgid="402819589201138227">"La información se usará 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>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hasta la carga completa"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Cargar"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carga rápida"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Carga completa"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Carga pausada"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada por el administrador"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlado por ajustes restringidos"</string>
<string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Una barra de cobertura"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Dos barras de cobertura"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Tres barras de cobertura"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Cobertura al máximo"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Sin datos"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Una barra de datos"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 9f77bd9..57ce62a 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiivne, ainult vasak"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiivne, ainult parem"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiivne, vasak ja parem"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktiivne (ainult meedia), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> aku täituvus"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktiivne (ainult meedia), V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> aku täituvus, P: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> aku täituvus"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Ühendatud (toetab heli jagamist), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> aku täituvus"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Ühendatud (toetab heli jagamist), V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> aku täituvus, P: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> aku täituvus"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Ühendatud (toetab heli jagamist), vasak <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Ühendatud (toetab heli jagamist), parem <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktiivne (ainult meedia)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Toetab heli jagamist"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktiivne (ainult meedia), ainult vasak"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktiivne (ainult meedia), ainult parem"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktiivne (ainult meedia), vasak ja parem"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Meediaheli"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonikõned"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failiedastus"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – täislaadimiseks kulub <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine on optimeeritud"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tundmatu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laadimine"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string>
@@ -505,10 +502,14 @@
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Juhtmevaba laadimine"</string>
<string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Laadimine"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei lae"</string>
- <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Ühendatud, kuid ei laadita"</string>
+ <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Ühendatud, kuid ei laeta"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Laetud"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Täielikult laetud"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Laadimine on ootele pandud"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Juhib administraator"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Haldavad piiranguga seaded"</string>
<string name="disabled" msgid="8017887509554714950">"Keelatud"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefonisignaal: üks pulk."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefonisignaal: kaks pulka."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefonisignaal: kolm pulka."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefonisignaal on tugev."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Andmed puuduvad."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Andmesignaal: üks pulk."</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 05f3ac7..781457d 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -106,32 +106,21 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktibo, ezkerrekoa soilik"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktibo, eskuinekoa soilik"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktibo, ezkerreko eta eskuineko audifonoak"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktibo (multimedia-edukia soilik); bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktibo (multimedia-edukia soilik); L aldearen bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. R aldearen bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Konektatuta (audioa partekatzeko eginbidea onartzen du); bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Konektatuta (audioa partekatzeko eginbidea onartzen du); L aldearen bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. R aldearen bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Konektatuta (audioa partekatzeko eginbidea onartzen du); ezkerreko aldearen bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Konektatuta (audioa partekatzeko eginbidea onartzen du); eskuineko aldearen bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktibo (multimedia-edukia soilik)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Audioa partekatzeko eginbidea onartzen du"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktibo (multimedia-edukia soilik); ezkerreko aldea soilik"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktibo (multimedia-edukia soilik); eskuineko aldea soilik"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktibo (multimedia-edukia soilik); ezkerreko eta eskuineko aldeak"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Euskarriaren audioa"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefono-deiak"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fitxategi-transferentzia"</string>
- <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sarrerako gailua"</string>
+ <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sarrera-gailua"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Interneteko konexioa"</string>
<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>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kargatzeko modu optimizatua"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Kargatzen"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ezezaguna"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Kargatzen"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Kargatuta"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Erabat kargatuta"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Kargatze-prozesua zain dago"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Administratzaileak kontrolatzen du"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ezarpen mugatuak kontrolatzen du"</string>
<string name="disabled" msgid="8017887509554714950">"Desgaituta"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefono-seinaleak barra bat du."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefono-seinaleak bi barra ditu."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefono-seinaleak hiru barra ditu."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefono-seinale osoa."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Ez dago daturik."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Datu-seinaleak barra bat du."</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 658fb0f..f9bab28 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"فعال، فقط چپ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"فعال، فقط راست"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"فعال، چپ و راست"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"فعال (فقط رسانه)، <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> باتری"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"فعال (فقط رسانه)، چپ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> باتری، راست: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> باتری"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"متصل (از اشتراک صدا پشتیبانی میکند)، <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> باتری"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"متصل (از اشتراک صدا پشتیبانی میکند)، چپ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> باتری، راست: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> باتری"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"متصل (از اشتراک صدا پشتیبانی میکند)، چپ<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"متصل (از اشتراک صدا پشتیبانی میکند)، راست <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"فعال (فقط رسانه)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"از اشتراک صدا پشتیبانی میکند"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"فعال (فقط رسانه)، فقط چپ"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"فعال (فقط رسانه)، فقط راست"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"فعال (فقط رسانه)، چپ و راست"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"رسانه صوتی"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"تماسهای تلفنی"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"انتقال فایل"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل باقی مانده است"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - شارژ بهینه شده است"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - درحال شارژ"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ناشناس"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"در حال شارژ شدن"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"درحال شارژ شدن سریع"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"شارژ کامل شد"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"کاملاً شارژ شده است"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"شارژ موقتاً متوقف شده است"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"توسط سرپرست سیستم کنترل میشود"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"با تنظیم «حالت محدود» کنترل میشود"</string>
<string name="disabled" msgid="8017887509554714950">"غیر فعال شد"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"یک نوار برای تلفن."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"دو نوار برای تلفن."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"سه نوار برای تلفن."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"قدرت امواج تلفن همراه کامل است."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"دادهای وجود ندارد."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"یک نوار برای داده."</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 14b1701..7377d0d 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiivinen, vain vasen"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiivinen, vain oikea"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiivinen, vasen ja oikea"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktiivinen (vain media), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> virtaa"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktiivinen (vain media), vasen: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> virtaa, oikea: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> virtaa"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Yhdistetty (tukee audionjakoa), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> virtaa"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Yhdistetty (tukee audionjakoa), vasen: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> virtaa, oikea: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> virtaa"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Yhdistetty (tukee audionjakoa), vasen: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Yhdistetty (tukee audionjakoa), oikea: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktiivinen (vain media)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Tukee audionjakoa"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktiivinen (vain media), vain vasen"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktiivinen (vain media), vain oikea"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktiivinen (vain media), vasen ja oikea"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Median ääni"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Puhelut"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Tiedostonsiirto"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Lataus optimoitu"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladataan"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tuntematon"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Ladataan"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Nopea lataus"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Ladattu"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Täyteen ladattu"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Lataus on pidossa"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Järjestelmänvalvoja hallinnoi tätä asetusta."</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Rajoitettujen asetusten mukaisesti"</string>
<string name="disabled" msgid="8017887509554714950">"Pois päältä"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Puhelinverkkosignaali - yksi palkki."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Puhelinverkkosignaali - kaksi palkkia."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Puhelinverkkosignaali - kolme palkkia."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Vahva puhelinverkkosignaali."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Ei datasignaalia."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Datasignaali - yksi palkki."</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index e4ae145..bfd7d9c 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Actif, gauche seulement"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Active, droite seulement"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Active, gauche et droite"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Actif (contenu multimédia uniquement), pile à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Actif (contenu multimédia uniquement), G. : pile à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D. : pile à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Connecté (prise en charge du partage audio), pile à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Connecté (prise en charge du partage audio), G. : pile à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D. : pile à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Connecté (prise en charge du partage audio), côté gauche : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Connecté (prise en charge du partage audio), côté droit : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Actif (contenu multimédia uniquement)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Prise en charge du partage audio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Actif (contenu multimédia uniquement), côté gauche seulement"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Actif (contenu multimédia uniquement), côté droit seulement"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Actif (contenu multimédia uniquement), côtés gauche et droit"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Paramètres audio du support"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Appels téléphoniques"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichier"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la recharge complète)"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Recharge en cours…"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charge en cours…"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Complètement rechargée"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Recharge en pause"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Contrôlé par l\'administrateur"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Contrôlé par les paramètres restreints"</string>
<string name="disabled" msgid="8017887509554714950">"Désactivée"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Signal : faible"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Signal : moyen"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Signal : bon"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Signal excellent"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Aucun signal"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Signal faible"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 09b40ac..846cc99 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Actif, gauche uniquement"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Actif, droit uniquement"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Actifs, gauche et droit"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Activé (multimédia uniquement), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batterie"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Activé (multimédia uniquement), gauche : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batterie, droit : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batterie"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Connecté (compatible avec le partage audio), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batterie"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Connecté (compatible avec le partage audio), gauche : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batterie, droit : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batterie"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Connecté (compatible avec le partage audio), gauche <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Connecté (compatible avec le partage audio), droit <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Activé (multimédia uniquement)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Compatible avec le partage audio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Activé (multimédia uniquement), gauche uniquement"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Activé (multimédia uniquement), droit uniquement"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Activé (multimédia uniquement), gauche et droit"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Audio multimédia"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Appels téléphoniques"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichiers"</string>
@@ -339,7 +328,7 @@
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Tailles enreg. par tampon journal"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"Effacer l\'espace de stockage persistant de l\'enregistreur ?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"Lorsque nous n\'effectuons plus de suivi avec l\'enregistreur persistant, nous sommes tenus d\'effacer les données associées à ce dernier qui sont stockées sur votre appareil."</string>
- <string name="select_logpersist_title" msgid="447071974007104196">"Stocker données enregistreur en permanence sur appareil"</string>
+ <string name="select_logpersist_title" msgid="447071974007104196">"Stocker données journaux sur appareil de manière persistante"</string>
<string name="select_logpersist_dialog_title" msgid="7745193591195485594">"Sélectionner les mémoires tampon journal à stocker en permanence sur l\'appareil"</string>
<string name="select_usb_configuration_title" msgid="6339801314922294586">"Sélectionner une configuration USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"Sélectionner une configuration USB"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - En charge"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Batterie en charge"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charge rapide"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Complètement chargée"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Recharge en pause"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Contrôlé par l\'administrateur"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Contrôlé par les paramètres restreints"</string>
<string name="disabled" msgid="8017887509554714950">"Désactivée"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Signal : faible"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Signal : moyen"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Signal : bon"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Signal excellent"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Aucun signal"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Signal faible"</string>
@@ -718,7 +721,7 @@
<string name="back_navigation_animation_summary" msgid="741292224121599456">"Activer les animations système pour la prévisualisation du Retour"</string>
<string name="back_navigation_animation_dialog" msgid="8696966520944625596">"Ce paramètre active les animations système pour la prévisualisation du geste de retour. Pour cela, enableOnBackInvokedCallback doit être défini sur \"True\" dans le fichier manifeste de chaque appli."</string>
<string name="font_scale_percentage" msgid="2624057443622817886">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
- <string name="not_specified" msgid="5423502443185110328">"Non défini"</string>
+ <string name="not_specified" msgid="5423502443185110328">"Non personnalisé"</string>
<string name="neuter" msgid="2075249330106127310">"Neutre"</string>
<string name="feminine" msgid="1529155595310784757">"Féminin"</string>
<string name="masculine" msgid="4653978041013996303">"Masculin"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index db40500..ffd4e6a 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Activo (só o esquerdo)"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Activo (só o dereito)"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Activos (o esquerdo e o dereito)"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Activo (só contido multimedia), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Activo (só contido multimedia), E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Conectado (compatible con audio compartido), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Conectado (compatible con audio compartido), E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Conectado (compatible con audio compartido), esquerdo<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Conectado (compatible con audio compartido), dereito <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Activo (só contido multimedia)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Compatible con audio compartido"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Activo (só contido multimedia), só esquerdo"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Activo (só contido multimedia), só dereito"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Activo (só contido multimedia), esquerdo e dereito"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Audio multimedia"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Chamadas telefónicas"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de ficheiros"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> para completar a carga)"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> (carga optimizada)"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (cargando)"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Descoñecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Carga completa"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Carga en pausa"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Opción controlada polo administrador"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Baixo o control de opcións restrinxidas"</string>
<string name="disabled" msgid="8017887509554714950">"Desactivada"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Unha barra de cobertura"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Dúas barras de cobertura"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Tres barras de cobertura"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Cobertura ao máximo"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Sen datos"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Unha barra de sinal de datos"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index c1eeca5..c1746b3 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"સક્રિય, માત્ર ડાબું"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"સક્રિય, માત્ર જમણું"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"સક્રિય, ડાબું અને જમણું બન્ને"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"સક્રિય છે (માત્ર મીડિયા માટે), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> બૅટરી"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"સક્રિય છે (માત્ર મીડિયા માટે), ડાબી બાજુ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> બૅટરી, જમણી બાજુ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> બૅટરી"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"કનેક્ટેડ છે (ઑડિયો શેરિંગને સપોર્ટ કરે છે), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> બૅટરી"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"કનેક્ટેડ છે (ઑડિયો શેરિંગને સપોર્ટ કરે છે), ડાબી બાજુ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> બૅટરી, જમણી બાજુ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> બૅટરી"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"કનેક્ટેડ છે (ઑડિયો શેરિંગને સપોર્ટ કરે છે), ડાબી બાજુ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"કનેક્ટેડ છે (ઑડિયો શેરિંગને સપોર્ટ કરે છે), જમણી બાજુ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"સક્રિય છે (માત્ર મીડિયા માટે)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ઑડિયો શેરિંગને સપોર્ટ કરે છે"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"સક્રિય છે (માત્ર મીડિયા માટે), માત્ર ડાબી બાજુ"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"સક્રિય છે (માત્ર મીડિયા માટે), માત્ર જમણી બાજુ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"સક્રિય છે (માત્ર મીડિયા માટે), ડાબી અને જમણી બાજુ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"મીડિયા ઑડિયો"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ફોન કૉલ"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ફાઇલ સ્થાનાંતરણ"</string>
@@ -335,7 +324,7 @@
<string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"આ મોડ ચાલુ કરેલો હોય ત્યારે MAC રેન્ડમાઇઝેશન ચાલુ કરેલું હોય તેવા નેટવર્ક સાથે આ ડિવાઇસ કનેક્ટ થશે, ત્યારે દર વખતે તેનું MAC ઍડ્રેસ બદલાય તેવું બની શકે છે."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"મીટર કરેલું"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"મીટર ન કરેલ"</string>
- <string name="select_logd_size_title" msgid="1604578195914595173">"લોગર બફર કદ"</string>
+ <string name="select_logd_size_title" msgid="1604578195914595173">"લૉગર બફર કદ"</string>
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"લૉગ દીઠ લૉગર કદ બફર પસંદ કરો"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"લૉગર નિરંતર સ્ટોરેજ સાફ કરીએ?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"જ્યારે અમે હવે નિરંતર લૉગર સાથે મોનીટર કરતાં નથી, તો તમારા ઉપકરણ પર રહેલો લૉગર ડેટા કાઢી નાખવાની જરૂર છે."</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%2$s</xliff:g> બાકી છે"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ ઑપ્ટિમાઇઝ કરવામાં આવ્યું છે"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"અજાણ્યું"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ચાર્જ થઈ રહ્યું છે"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"ચાર્જ થયું"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"સંપૂર્ણપણે ચાર્જ છે"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ચાર્જિંગ હોલ્ડ પર રાખવામાં આવ્યું"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"વ્યવસ્થાપક દ્વારા નિયંત્રિત"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"પ્રતિબંધિત સેટિંગ દ્વારા નિયંત્રિત"</string>
<string name="disabled" msgid="8017887509554714950">"અક્ષમ કર્યો"</string>
@@ -553,7 +554,7 @@
<string name="okay" msgid="949938843324579502">"ઓકે"</string>
<string name="done" msgid="381184316122520313">"થઈ ગયું"</string>
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"અલાર્મ અને રિમાઇન્ડર"</string>
- <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"અલાર્મ અને રિમાન્ડરના સેટિંગની મંજૂરી આપો"</string>
+ <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"અલાર્મ અને રિમાઇન્ડરના સેટિંગની મંજૂરી આપો"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"અલાર્મ અને રિમાઇન્ડર"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"આ ઍપને અલાર્મ સેટ કરવા અને સમય પ્રતિ સંવેદનશીલ ક્રિયાઓ શેડ્યૂલ કરવા માટે મંજૂરી આપો. આ ઍપને બૅકગ્રાઉન્ડમાં ચાલવા દે છે, જેને કારણે બૅટરીનો વધુ વપરાશ થઈ શકે છે.\n\nજો આ પરવાનગી બંધ હોય, તો આ ઍપ દ્વારા શેડ્યૂલ કરવામાં આવેલા વર્તમાન અલાર્મ અને સમય આધારિત ઇવેન્ટ કામ કરશે નહીં."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"શેડ્યૂલ, અલાર્મ, રિમાઇન્ડર, ઘડિયાળ"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"ફોન એક બાર."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"ફોન બે બાર."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"ફોન ત્રણ બાર."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"પૂર્ણ ફોન સિગ્નલ."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"કોઈ ડેટા નથી."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"ડેટા એક બાર."</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index a5b3c88..99dcbb0 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"सिर्फ़ बाईं तरफ़ वाला चालू है"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"सिर्फ़ दाईं तरफ़ वाला चालू है"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"बाईं और दाईं तरफ़ वाला चालू है"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"चालू है (सिर्फ़ मीडिया के लिए), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> बैटरी"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"चालू है (सिर्फ़ मीडिया के लिए), बायां हेडसेट: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> बैटरी, दायां हेडसेट: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> बैटरी"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"कनेक्ट हो गया (ऑडियो शेयर करने की सुविधा काम करती है), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> बैटरी"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"कनेक्ट हो गया (ऑडियो शेयर करने की सुविधा काम करती है), बायां हेडसेट: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> बैटरी, दायां हेडसेट: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> बैटरी"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"कनेक्ट हो गया (ऑडियो शेयर करने की सुविधा काम करती है), बायां हेडसेट<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"कनेक्ट हो गया (ऑडियो शेयर करने की सुविधा काम करती है), दायां हेडसेट <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"चालू है (सिर्फ़ मीडिया के लिए)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ऑडियो शेयर करने की सुविधा काम करती है"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"चालू है (सिर्फ़ मीडिया के लिए), सिर्फ़ बाएं कान की मशीन"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"चालू है (सिर्फ़ मीडिया के लिए), सिर्फ़ दाएं कान की मशीन"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"चालू है (सिर्फ़ मीडिया के लिए), बाएं और दाएं कान की मशीन"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"मीडिया ऑडियो"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"फ़ोन कॉल"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"फ़ाइल स्थानांतरण"</string>
@@ -203,7 +192,7 @@
<string name="launch_defaults_none" msgid="8049374306261262709">"कोई डिफ़ॉल्ट सेट नहीं है"</string>
<string name="tts_settings" msgid="8130616705989351312">"लेख से बोली सेटिंग"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"लिखे गए शब्दों को सुनने की सुविधा"</string>
- <string name="tts_default_rate_title" msgid="3964187817364304022">"बोलने की दर"</string>
+ <string name="tts_default_rate_title" msgid="3964187817364304022">"बोलने की स्पीड"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"बोलने की गति तय करें"</string>
<string name="tts_default_pitch_title" msgid="6988592215554485479">"पिच"</string>
<string name="tts_default_pitch_summary" msgid="9132719475281551884">"कृत्रिम बोली के लहजे को प्रभावित करता है"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> में बैटरी पूरी चार्ज हो जाएगी"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग को ऑप्टिमाइज़ किया गया"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज हो रही है"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हो रही है"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"तेज़ चार्ज हो रही है"</string>
@@ -509,12 +506,16 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"बैटरी चार्ज हो गई"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"बैटरी पूरी चार्ज है"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"फ़ोन को चार्ज होने से रोक दिया गया है"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"इसका नियंत्रण एडमिन के पास है"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"इसे पाबंदी मोड वाली सेटिंग से कंट्रोल किया जाता है"</string>
<string name="disabled" msgid="8017887509554714950">"बंद किया गया"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"अनुमति है"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"अनुमति नहीं है"</string>
- <string name="install_other_apps" msgid="3232595082023199454">"अज्ञात ऐप्लिकेशन इंस्टॉल करने की अनुमति देना"</string>
+ <string name="install_other_apps" msgid="3232595082023199454">"अनजान ऐप्लिकेशन इंस्टॉल करने की अनुमति"</string>
<string name="home" msgid="973834627243661438">"सेटिंग का होम पेज"</string>
<string-array name="battery_labels">
<item msgid="7878690469765357158">"0%"</item>
@@ -537,7 +538,7 @@
<string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"सिस्टम की भाषाओं का इस्तेमाल करें"</string>
<string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> के लिए सेटिंग खोलने में विफल रहा"</string>
<string name="ime_security_warning" msgid="6547562217880551450">"इनपुट का यह तरीका, आपके पासवर्ड और क्रेडिट कार्ड नंबर जैसे निजी डेटा के साथ-साथ उस सभी डेटा को इकट्ठा कर सकता है जिसे आप लिखते हैं. यह <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ऐप्लिकेशन से आता है. इनपुट के इस तरीके का इस्तेमाल करें?"</string>
- <string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"नोट: पुनः बूट करने के बाद, यह ऐप्लिकेशन तब तक शुरू नहीं हो सकता है जब तक कि आप अपना फ़ोन अनलॉक ना कर लें"</string>
+ <string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"ध्यान दें: डिवाइस को फिर से चालू करने पर, यह ऐप्लिकेशन तब तक शुरू नहीं होगा, जब तक आप अपना फ़ोन अनलॉक न कर लें"</string>
<string name="ims_reg_title" msgid="8197592958123671062">"IMS रजिस्ट्रेशन की स्थिति"</string>
<string name="ims_reg_status_registered" msgid="884916398194885457">"रजिस्टर है"</string>
<string name="ims_reg_status_not_registered" msgid="2989287366045704694">"रजिस्टर नहीं है"</string>
@@ -616,7 +617,7 @@
<string name="user_add_profile_item_title" msgid="3111051717414643029">"प्रतिबंधित प्रोफ़ाइल"</string>
<string name="user_add_user_title" msgid="5457079143694924885">"नया उपयोगकर्ता जोड़ें?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"नए उपयोगकर्ता जोड़कर इस डिवाइस को दूसरे लोगों के साथ शेयर किया जा सकता है. हर उपयोगकर्ता के पास अपनी जगह होती है, जिसमें वे ऐप्लिकेशन, वॉलपेपर, और दूसरी चीज़ों में मनमुताबिक बदलाव कर सकते हैं. उपयोगकर्ता, वाई-फ़ाई जैसी डिवाइस सेटिंग में भी बदलाव कर सकते हैं. इसका असर हर किसी पर पड़ता है.\n\nजब किसी नए उपयोगकर्ता को जोड़ा जाता है, तो उसे अपनी जगह सेट अप करनी होती है.\n\nकोई भी उपयोगकर्ता, दूसरे सभी उपयोगकर्ताओं के लिए ऐप्लिकेशन अपडेट कर सकता है. ऐसा भी हो सकता है कि सुलभता सेटिंग और सेवाएं नए उपयोगकर्ता को ट्रांसफ़र न हो पाएं."</string>
- <string name="user_add_user_message_short" msgid="3295959985795716166">"कोई नया उपयोगकर्ता जोड़ने पर, उसे अपनी जगह सेट करनी होती है.\n\nकोई भी उपयोगकर्ता, बाकी सभी उपयोगकर्ताओं के लिए ऐप्लिकेशन अपडेट कर सकता है."</string>
+ <string name="user_add_user_message_short" msgid="3295959985795716166">"जब किसी नए उपयोगकर्ता को जोड़ा जाता है, तो उस व्यक्ति को डिवाइस में अपनी जगह सेट करनी होती है.\n\nकोई भी उपयोगकर्ता, बाकी सभी उपयोगकर्ताओं के लिए ऐप्लिकेशन अपडेट कर सकता है."</string>
<string name="user_grant_admin_title" msgid="5157031020083343984">"क्या इस व्यक्ति को एडमिन बनाना है?"</string>
<string name="user_grant_admin_message" msgid="1673791931033486709">"एडमिन के पास अन्य लोगों के मुकाबले खास अधिकार होते हैं. एडमिन के पास ये अधिकार होते हैं: सभी लोगों को मैनेज करना, इस डिवाइस को अपडेट या रीसेट करना, सेटिंग में बदलाव करना, इंस्टॉल किए गए सभी ऐप्लिकेशन देखना, और अन्य लोगों को एडमिन के खास अधिकार देना या उन्हें वापस लेना."</string>
<string name="user_grant_admin_button" msgid="5441486731331725756">"एडमिन बनाएं"</string>
@@ -638,7 +639,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"नया उपयोगकर्ता जोड़ा नहीं जा सका"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"नया मेहमान खाता नहीं बनाया जा सका"</string>
<string name="user_nickname" msgid="262624187455825083">"निकनेम"</string>
- <string name="edit_user_info_message" msgid="6677556031419002895">"आपकी चुनी गई फ़ोटो और नाम, उन सभी लोगों को दिखेगा जो इस डिवाइस का इस्तेमाल करते हैं."</string>
+ <string name="edit_user_info_message" msgid="6677556031419002895">"आपका नाम और चुनी गई फ़ोटो, इस डिवाइस का इस्तेमाल करने वाले हर व्यक्ति को दिखेगी."</string>
<string name="user_add_user" msgid="7876449291500212468">"उपयोगकर्ता जोड़ें"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान को हटाएं"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"फ़ोन एक बार."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"फ़ोन दो बार."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"फोन तीन बार."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"फ़ोन सिग्नल पूरा."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"कोई डेटा नहीं."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"डेटा एक बार."</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index a504c9a0..a1dcf83 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -33,7 +33,7 @@
<string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Ništa/OWE"</string>
<string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
<string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
- <string name="wifi_security_none" msgid="7392696451280611452">"Nema"</string>
+ <string name="wifi_security_none" msgid="7392696451280611452">"Ništa"</string>
<string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
<string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
<string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktivno, samo lijevo"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktivno, samo desno"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktivno, lijevo i desno"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktivno (samo medijski sadržaji), razina baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktivno (samo medijski sadržaji), L: razina baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: razina baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Povezano (podržava zajedničko slušanje), razina baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Povezano (podržava zajedničko slušanje), L: razina baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: razina baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Povezano (podržava zajedničko slušanje), lijeva <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Povezano (podržava zajedničko slušanje), desna <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktivno (samo medijski sadržaji)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Podržava zajedničko slušanje"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktivno (samo medijski sadržaji), samo lijeva"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktivno (samo medijski sadržaji), samo desna"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktivno (samo medijski sadržaji), lijeva i desna"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Zvuk medija"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonski pozivi"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prijenos datoteke"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje se optimizira"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Posve puna"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Punjenje na čekanju"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolira administrator"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolira ograničena postavka"</string>
<string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefonski signal jedan stupac."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefonski signal dva stupca."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefonski signal tri stupca."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefonski signal pun."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Nema podataka."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Podatkovni signal jedan stupac."</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 3838d7d0..7133353 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktív, csak bal"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktív, csak jobb"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktív, bal és jobb"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktív (csak médiatartalom lejátszása), akkumulátor töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktív (csak médiatartalom lejátszása), bal eszköz akkumulátorának töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, jobb eszköz akkumulátorának töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Csatlakoztatva (támogatja a hang megosztását), akkumulátor töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Csatlakoztatva (támogatja a hang megosztását), bal eszköz akkumulátorának töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, jobb eszköz akkumulátorának töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Csatlakoztatva (támogatja a hang megosztását), bal eszköz akkumulátorának töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Csatlakoztatva (támogatja a hang megosztását), jobb eszköz akkumulátorának töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktív (csak médiatartalom lejátszása)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Támogatja a hang megosztását"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktív (csak médiatartalom lejátszása), csak a bal"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktív (csak médiatartalom lejátszása), csak a jobb"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktív (csak médiatartalom lejátszása), bal és jobb"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Médiahang"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonhívások"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fájlátvitel"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes töltöttségig"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimalizált töltés"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Töltés…"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ismeretlen"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Töltés"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Gyorstöltés"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Feltöltve"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Teljesen feltöltve"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"A töltés szünetel"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Rendszergazda által irányítva"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Korlátozott beállítás vezérli"</string>
<string name="disabled" msgid="8017887509554714950">"Letiltva"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefon egy sáv."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefon két sáv."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefon három sáv."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefonjel megtelt."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Nincsenek adatok."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Adat egy sáv."</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 0069175..160cb79 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Ակտիվ, միայն ձախ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Ակտիվ, միայն աջ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Ակտիվ, ձախ և աջ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Ակտիվ է (միայն մեդիա), մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Ակտիվ է (միայն մեդիա), Ձ՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Ա՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Միացված է (աջակցում է աուդիոյի փոխանցում), մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Միացված է (աջակցում է աուդիոյի փոխանցում), Ձ՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Ա՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Միացված է (աջակցում է աուդիոյի փոխանցում), ձախ՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Միացված է (աջակցում է աուդիոյի փոխանցում), աջ՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Ակտիվ է (միայն մեդիա)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Աջակցում է աուդիոյի փոխանցում"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Ակտիվ է (միայն մեդիա), միայն ձախ"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Ակտիվ է (միայն մեդիա), միայն աջ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Ակտիվ է (միայն մեդիա), աջ և ձախ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Մեդիա աուդիո"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Հեռախոսազանգեր"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Ֆայլերի փոխանցում"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորումն օպտիմալացված է"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> — Լիցքավորում"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Անհայտ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Լիցքավորում"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Լիցքավորված է"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Լրիվ լիցքավորված է"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Լրցքավորումը դադարեցված է"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Վերահսկվում է ադմինիստրատորի կողմից"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Կառավարվում է սահմանափակ ռեժիմի կարգավորումներով"</string>
<string name="disabled" msgid="8017887509554714950">"Կասեցված է"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Հեռախոսի մեկ գիծ:"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Հեռախոսի երկու գիծ:"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Հեռախոսի երեք գիծ:"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Հեռախոսի ազդանշանը լիքն է:"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Տվյալներ չկան:"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Տվյալների մեկ գիծ:"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 6254f7b..7ee46cf 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -106,32 +106,21 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktif, hanya kiri"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktif, hanya kanan"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktif, kiri dan kanan"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktif (hanya media), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterai"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktif (hanya media), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> baterai, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterai"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Terhubung (mendukung berbagi audio), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterai"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Terhubung (mendukung berbagi audio), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> baterai, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterai"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Terhubung (mendukung berbagi audio), kiri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Terhubung (mendukung berbagi audio), kanan <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktif (hanya media)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Mendukung berbagi audio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktif (hanya media), hanya kiri"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktif (hanya media), hanya kanan"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktif (hanya media), kiri dan kanan"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Audio media"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Panggilan telepon"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfer file"</string>
- <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Perangkat masukan"</string>
+ <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Perangkat input"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string>
<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 lain-lain"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi sampai penuh"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengisian daya dioptimalkan"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Mengisi daya"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Mengisi daya"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengisi daya cepat"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Terisi"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Baterai Terisi Penuh"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Pengisian daya dihentikan sementara"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Dikontrol oleh admin"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Dikontrol oleh Setelan Terbatas"</string>
<string name="disabled" msgid="8017887509554714950">"Dinonaktifkan"</string>
@@ -555,7 +556,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarm dan pengingat"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Izinkan menyetel alarm dan pengingat"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarm & pengingat"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Izinkan aplikasi ini menyetel alarm dan menjadwalkan tindakan yang sensitif waktu. Hal ini memungkinkan aplikasi berjalan di latar belakang, sehingga mungkin menggunakan lebih banyak daya baterai.\n\nJika izin ini dinonaktifkan, alarm dan acara berbasis waktu yang dijadwalkan oleh aplikasi ini tidak akan berfungsi."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Mengizinkan aplikasi ini menyetel alarm dan menjadwalkan tindakan yang sensitif waktu. Hal ini memungkinkan aplikasi berjalan di latar belakang, sehingga mungkin menggunakan lebih banyak daya baterai.\n\nJika izin ini dinonaktifkan, alarm dan acara berbasis waktu yang dijadwalkan oleh aplikasi ini tidak akan berfungsi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"jadwal, alarm, pengingat, jam"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktifkan"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktifkan mode Jangan Ganggu"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Ponsel satu batang."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Ponsel dua batang."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Ponsel tiga batang."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Sinyal ponsel penuh."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Tidak ada data yang diterima."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data satu batang."</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 0f57670..51d1803 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Virkt, aðeins vinstra"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Virkt, aðeins hægra"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Virkt, vinstra og hægra"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Virkt (eingöngu margmiðlunarefni), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> rafhlaða"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Virkt (eingöngu margmiðlunarefni), V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> rafhlaða, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> rafhlaða"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Tengt (styður hljóðdeilingu), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> rafhlaða"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Tengt (styður hljóðdeilingu), V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> rafhlaða, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> rafhlaða"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Tengt (styður hljóðdeilingu), vinstri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Tengt (styður hljóðdeilingu), hægri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Virkt (eingöngu margmiðlunarefni)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Styður hljóðdeilingu"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Virkt (eingöngu margmiðlunarefni), eingöngu vinstri"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Virkt (eingöngu margmiðlunarefni), eingöngu hægri"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Virkt (eingöngu margmiðlunarefni), vinstri og hægri"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Hljóð efnis"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Símtöl"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Skráaflutningur"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> fram að fullri hleðslu"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hleðsla fínstillt"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Í hleðslu"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Óþekkt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Í hleðslu"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Fullhlaðin"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Full hleðsla"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Hleðsla í bið"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Stjórnað af kerfisstjóra"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Stýrt af takmarkaði stillingu"</string>
<string name="disabled" msgid="8017887509554714950">"Óvirkt"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Styrkur símasambands er eitt strik."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Styrkur símasambands er tvö strik."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Styrkur símasambands er þrjú strik."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Fullur styrkur símasambands."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Engin gögn."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Sendistyrkur gagnatengingar er eitt strik."</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 9502374..e8c5cf5 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Attiva, solo sinistra"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Attiva, solo destra"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Attivo, destra e sinistra"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Attivo (solo contenuti multimediali), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> di batteria"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Attivo (solo contenuti multimediali), S: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> di batteria, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> di batteria"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Connesso (supporta la condivisione audio), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> di batteria"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Connesso (supporta la condivisione audio), S: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> di batteria, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> di batteria"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Connesso (supporta la condivisione audio), sinistro <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Connesso (supporta la condivisione audio), destro <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Attivo (solo contenuti multimediali)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Supporta la condivisione audio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Attivo (solo contenuti multimediali), solo sinistro"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Attivo (solo contenuti multimediali), solo destro"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Attivo (solo contenuti multimediali), sinistro e destro"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Audio multimediale"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonate"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Trasferimento file"</string>
@@ -167,7 +156,7 @@
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Annulla"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"L\'accoppiamento consente l\'accesso ai tuoi contatti e alla cronologia chiamate quando i dispositivi sono connessi."</string>
<string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Impossibile eseguire l\'accoppiamento con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
- <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Impossibile eseguire l\'accoppiamento con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. La password o il PIN sono errati."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Impossibile eseguire l\'accoppiamento con <xliff:g id="DEVICE_NAME">%1$s</xliff:g> perché la passkey o il PIN sono errati."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"Impossibile comunicare con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Accoppiamento rifiutato da <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_talkback_computer" msgid="3736623135703893773">"Computer"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla ricarica completa"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ricarica ottimizzata"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ In carica"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Sconosciuta"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"In carica"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ricarica veloce"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Carica"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Batteria completamente carica"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Ricarica in sospeso"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Gestita dall\'amministratore"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Gestita tramite impostazioni con restrizioni"</string>
<string name="disabled" msgid="8017887509554714950">"Disattivato"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefono: una barra."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefono: due barre."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefono: tre barre."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Massimo segnale telefonico."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Nessun dato."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Dati: una barra."</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index dea8ca3..72a025c 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -106,35 +106,24 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"פועל: שמאל בלבד"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"פועל: ימין בלבד"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"פועל: ימין ושמאל"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"פעיל (מדיה בלבד), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> סוללה"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"פעיל (מדיה בלבד), שמאל: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> סוללה, ימין: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> סוללה"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"מחובר (תמיכה בשיתוף אודיו), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> סוללה"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"מחובר (תמיכה בשיתוף אודיו), שמאל: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> סוללה, ימין: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> סוללה"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"מחובר (תמיכה בשיתוף אודיו), שמאל <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"מחובר (תמיכה בשיתוף אודיו), ימין <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"פעיל (מדיה בלבד)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"תמיכה בשיתוף אודיו"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"פעיל (מדיה בלבד), שמאל בלבד"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"פעיל (מדיה בלבד), ימין בלבד"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"פעיל (מדיה בלבד), שמאל וימין"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"אודיו של מדיה"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"שיחות טלפון"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"העברת קבצים"</string>
- <string name="bluetooth_profile_hid" msgid="2969922922664315866">"מכשיר קלט"</string>
+ <string name="bluetooth_profile_hid" msgid="2969922922664315866">"התקן קלט"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"גישה לאינטרנט"</string>
- <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"אישור גישה אל אנשי קשר והיסטוריית שיחות"</string>
- <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"המידע ישמש להודעות על שיחות ועוד"</string>
+ <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>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – הזמן הנותר לטעינה מלאה: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – הטעינה עברה אופטימיזציה"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – בטעינה"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"לא ידוע"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"בטעינה"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"הסוללה נטענת מהר"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"הסוללה טעונה"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"טעונה במלואה"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"הטעינה הושהתה"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"נמצא בשליטת מנהל מערכת"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"בשליטה של הגדרה מוגבלת"</string>
<string name="disabled" msgid="8017887509554714950">"מושבת"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"פס אחד של טלפון."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"שני פסים של טלפון."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"שלושה פסים של טלפון."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"אות הטלפון מלא."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"אין נתונים."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"פס אחד של נתונים."</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index c7f3c0a..80858ab 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"有効、左のみ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"有効、右のみ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"有効、左と右"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"有効(メディアのみ)、バッテリー残量 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"有効(メディアのみ)、左: バッテリー残量 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>、右: バッテリー残量 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"接続済み(音声の共有をサポート)、バッテリー残量 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"接続済み(音声の共有をサポート)、左: バッテリー残量 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>、右: バッテリー残量 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"接続済み(音声の共有をサポート)、左 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"接続済み(音声の共有をサポート)、右 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"有効(メディアのみ)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"音声の共有をサポートしています"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"有効(メディアのみ)、左のみ"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"有効(メディアのみ)、右のみ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"有効(メディアのみ)、左右"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"メディアの音声"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"電話"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ファイル転送"</string>
@@ -279,7 +268,7 @@
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"デバイスをペア設定しています…"</string>
<string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"デバイスをペア設定できませんでした。QR コードが間違っているか、デバイスが同じネットワークに接続されていません。"</string>
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP アドレスとポート"</string>
- <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR コードのスキャン"</string>
+ <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR コードをスキャン"</string>
<string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR コードをスキャンして Wi-Fi 経由でデバイスをペア設定します"</string>
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Wi-Fi ネットワークに接続してください"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, デバッグ, dev"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 完了まであと <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電が最適化されています"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電中"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"急速充電中"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"充電が完了しました"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"充電完了"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"充電を一時停止しています"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"管理者により管理されています"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"制限付き設定によって管理されています"</string>
<string name="disabled" msgid="8017887509554714950">"無効"</string>
@@ -666,9 +667,9 @@
<string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"終了時にアクティビティを保存、削除できます"</string>
<string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"アクティビティは、リセットして今すぐ削除するか、終了時に保存または削除できます"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"写真を選択"</string>
- <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"間違えた回数が上限を超えました。このデバイスのデータが削除されます。"</string>
- <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"間違えた回数が上限を超えました。このユーザーが削除されます。"</string>
- <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"間違えた回数が上限を超えました。この仕事用プロファイルと関連データが削除されます。"</string>
+ <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"試行回数が上限に達しました。このデバイスのデータが削除されます。"</string>
+ <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"試行回数が上限に達しました。このユーザーが削除されます。"</string>
+ <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"試行回数が上限に達しました。この仕事用プロファイルと関連データが削除されます。"</string>
<string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"閉じる"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"デバイスのデフォルト"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"無効"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"電波状態:レベル1"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"電波状態:レベル2"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"電波状態:レベル3"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"電波状態:フル"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"データ信号:なし"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"データ信号:レベル1"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index a94a401..c422b73 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"აქტიური, მხოლოდ მარცხნივ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"აქტიური, მხოლოდ მარჯვნივ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"აქტიური, მარცხნივ და მარჯვნივ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"აქტიური (მხოლოდ მედია), ბატარეა: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"აქტიური (მხოლოდ მედია), მარცხენა: ბატარეა <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, მარჯვენა: ბატარეა<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"დაკავშირებული (აუდიოს გაზიარება მხარდაჭერილია), ბატარეა <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"დაკავშირებული (აუდიოს გაზიარება მხარდაჭერილია), მარცხენა: ბატარეა <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, მარჯვენა: ბატარეა <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"დაკავშირებული (აუდიოს გაზიარება მხარდაჭერილია), მარცხენა: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"დაკავშირებული (აუდიოს გაზიარება მხარდაჭერილია), მარჯვენა: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"აქტიური (მხოლოდ მედია)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"აუდიოს გაზიარება მხარდაჭერილია"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"აქტიური (მხოლოდ მედია), მხოლოდ მარცხენა"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"აქტიური (მხოლოდ მედია), მხოლოდ მარჯვენა"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"აქტიური (მხოლოდ მედია), მარცხენა და მარჯვენა"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"მედია აუდიო"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"სატელეფონო ზარები"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ფაილების გადაცემა"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> — სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - დატენვა ოპტიმიზირებულია"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – იტენება"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"უცნობი"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"იტენება"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"სწრაფად იტენება"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"დატენილია"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"ბოლომდე დატენილი"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"დატენვა შეჩერებულია"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"იმართება ადმინისტრატორის მიერ"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"კონტროლდება შეზღუდული რეჟიმის პარამეტრით"</string>
<string name="disabled" msgid="8017887509554714950">"გამორთული"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"ტელეფონის სიგნალი ერთ ზოლზეა."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"ტელეფონის სიგნალი ორ ზოლზეა."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"ტელეფონის სიგნალი სამ ზოლზეა."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"ტელეფონის სიგნალი სრულია."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"მონაცემები არ არის."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"თარიღი ზოლზე."</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 1c80ad4..bac01d9 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -106,32 +106,21 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Тек сол жағы қосулы"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Тек оң жағы қосулы"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Екеуі де қосулы"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Істеп тұр (тек мультимедиа), батарея зарядының деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Істеп тұр (тек мультимедиа). Сол жақ: батарея зарядының деңгейі — <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. Оң жақ: батарея зарядының деңгейі — <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Қосылды (аудио бөлісуге мүмкіндік береді), батарея зарядының деңгейі:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Қосылды (аудио бөлісуге мүмкіндік береді). Сол жақ: батарея зарядының деңгейі — <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. Оң жақ: батарея зарядының деңгейі — <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Қосылды (аудио бөлісуге мүмкіндік береді), сол жақ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Қосылды (аудио бөлісуге мүмкіндік береді), оң жақ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Істеп тұр (тек мультимедиа)."</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Аудио бөлісуге мүмкіндік береді."</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Тек сол жақ істеп тұр (мультимедиа ғана)."</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Тек оң жақ істеп тұр (мультимедиа ғана)."</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Сол және оң жақ істеп тұр (мультимедиа ғана)."</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Mультимeдиа дыбысы"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Телефон қоңыраулары"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл жіберу"</string>
- <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Кіріс құрылғысы"</string>
+ <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Енгізу құрылғысы"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернетке қосылу"</string>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Контакт пен қоңырау тарихына рұқсат беру"</string>
<string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Ақпарат қоңырау туралы хабарландыру, т.б. үшін қолданылады."</string>
@@ -208,7 +197,7 @@
<string name="tts_default_pitch_title" msgid="6988592215554485479">"Дауыс жиілігі"</string>
<string name="tts_default_pitch_summary" msgid="9132719475281551884">"Синтезделген сөйлеу үніне әсер етеді"</string>
<string name="tts_default_lang_title" msgid="4698933575028098940">"Тіл"</string>
- <string name="tts_lang_use_system" msgid="6312945299804012406">"Жүйелік тілді пайдалану"</string>
+ <string name="tts_lang_use_system" msgid="6312945299804012406">"Жүйе тілін пайдалану"</string>
<string name="tts_lang_not_selected" msgid="7927823081096056147">"Тіл таңдалған жоқ"</string>
<string name="tts_default_lang_summary" msgid="9042620014800063470">"Сөйлеу мәтіні үшін тілге тән дауыс орнатады"</string>
<string name="tts_play_example_title" msgid="1599468547216481684">"Үлгіні тыңдау"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: толық зарядталуға <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядтау оңтайландырылды"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зарядталып жатыр"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Белгісіз"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарядталуда"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядтау"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Зарядталды"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Толық зарядталды."</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Зарядтау кідіртілді."</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Әкімші басқарады"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Шектелген параметрлер арқылы басқарылады."</string>
<string name="disabled" msgid="8017887509554714950">"Өшірілген"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Телефон бір баған."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Телефон екі баған."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Телефон үш баған."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Телефон сигналы толық."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Дерекқор жоқ."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Дерекқор бір баған."</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 23bd55c..0d41022 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"សកម្ម ខាងឆ្វេងតែប៉ុណ្ណោះ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"សកម្មខាងស្ដាំតែប៉ុណ្ណោះ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"សកម្មខាងឆ្វេង និងស្ដាំ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"សកម្ម (តែមេឌៀប៉ុណ្ណោះ) ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"សកម្ម (តែមេឌៀប៉ុណ្ណោះ) ឆ្វេង៖ ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ស្ដាំ៖ ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"បានភ្ជាប់ (អាចប្រើការស្ដាប់សំឡេងរួមគ្នា) ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"បានភ្ជាប់ (អាចប្រើការស្ដាប់សំឡេងរួមគ្នា) ឆ្វេង៖ ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ស្ដាំ៖ ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"បានភ្ជាប់ (អាចប្រើការស្ដាប់សំឡេងរួមគ្នា) ឆ្វេង <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"បានភ្ជាប់ (អាចប្រើការស្ដាប់សំឡេងរួមគ្នា) ស្ដាំ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"សកម្ម (តែមេឌៀប៉ុណ្ណោះ)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"អាចប្រើការស្ដាប់សំឡេងរួមគ្នា"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"សកម្ម (តែមេឌៀប៉ុណ្ណោះ) តែខាងឆ្វេងប៉ុណ្ណោះ"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"សកម្ម (តែមេឌៀប៉ុណ្ណោះ) តែខាងស្ដាំប៉ុណ្ណោះ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"សកម្ម (តែមេឌៀប៉ុណ្ណោះ) ឆ្វេង និងស្ដាំ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"សំឡេងមេឌៀ"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ការហៅទូរសព្ទ"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ផ្ទេរឯកសារ"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - នៅសល់ <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបពេញ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - បានបង្កើនប្រសិទ្ធភាពនៃការសាក"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - កំពុងសាកថ្ម"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"មិនស្គាល់"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"កំពុងសាកថ្ម"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"បានសាកថ្មពេញ"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"បានសាកថ្មពេញ"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"កំពុងផ្អាកការសាកថ្ម"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"គ្រប់គ្រងដោយអ្នកគ្រប់គ្រង"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"គ្រប់គ្រងដោយការកំណត់ដែលបានរឹតបន្តឹង"</string>
<string name="disabled" msgid="8017887509554714950">"បិទ"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"សេវាទូរស័ព្ទមួយកាំ។"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"សេវាទូរស័ព្ទពីរកាំ។"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"សេវាទូរស័ព្ទបីកាំ។"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"សេវាទូរស័ព្ទពេញ។"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"គ្មានទិន្នន័យ។"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"ទិន្នន័យមួយកាំ។"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index c636dd5..f28712f 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"ಎಡಕಿವಿಯ ಸಾಧನ ಮಾತ್ರ ಸಕ್ರಿಯವಾಗಿದೆ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"ಬಲಕಿವಿಯ ಸಾಧನ ಮಾತ್ರ ಸಕ್ರಿಯವಾಗಿದೆ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"ಎಡ ಮತ್ತು ಬಲಕಿವಿಯ ಸಾಧನಗಳು ಸಕ್ರಿಯವಾಗಿವೆ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"ಸಕ್ರಿಯವಾಗಿದೆ (ಮೀಡಿಯಾ ಮಾತ್ರ), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ಬ್ಯಾಟರಿ ಮಟ್ಟ"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"ಸಕ್ರಿಯವಾಗಿದೆ (ಮೀಡಿಯಾ ಮಾತ್ರ), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ಬ್ಯಾಟರಿ ಮಟ್ಟ, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ಬ್ಯಾಟರಿ ಮಟ್ಟ"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"ಕನೆಕ್ಟ್ ಆಗಿದೆ (ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ಬ್ಯಾಟರಿ ಮಟ್ಟ"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"ಕನೆಕ್ಟ್ ಆಗಿದೆ (ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ಬ್ಯಾಟರಿ ಮಟ್ಟ, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ಬ್ಯಾಟರಿ ಮಟ್ಟ"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"ಕನೆಕ್ಟ್ ಆಗಿದೆ (ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ), ಎಡ ಭಾಗದ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"ಕನೆಕ್ಟ್ ಆಗಿದೆ (ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ), ಬಲ ಭಾಗದ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"ಸಕ್ರಿಯವಾಗಿದೆ (ಮೀಡಿಯಾ ಮಾತ್ರ)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"ಸಕ್ರಿಯವಾಗಿದೆ (ಮೀಡಿಯಾ ಮಾತ್ರ), ಎಡ ಭಾಗದ ಮಾತ್ರ"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"ಸಕ್ರಿಯವಾಗಿದೆ (ಮೀಡಿಯಾ ಮಾತ್ರ), ಬಲ ಭಾಗದ ಮಾತ್ರ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"ಸಕ್ರಿಯವಾಗಿದೆ (ಮೀಡಿಯಾ ಮಾತ್ರ), ಎಡ ಮತ್ತು ಬಲ ಭಾಗದ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"ಮಾಧ್ಯಮ ಆಡಿಯೋ"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ಫೋನ್ ಕರೆಗಳು"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ಫೈಲ್ ವರ್ಗಾವಣೆ"</string>
@@ -162,7 +151,7 @@
<string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ಇನ್ಪುಟ್ಗಾಗಿ ಬಳಸು"</string>
<string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"ಶ್ರವಣ ಸಾಧನಗಳಿಗಾಗಿ ಬಳಸಿ"</string>
<string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO ಗೆ ಬಳಸಿ"</string>
- <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ಜೋಡಿಸಿ"</string>
+ <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ಪೇರ್ ಮಾಡಿ"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ಜೋಡಿ ಮಾಡು"</string>
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ರದ್ದುಮಾಡಿ"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"ಸಂಪರ್ಕಗೊಳಿಸಿದಾಗ, ಜೋಡಿಸುವಿಕೆಯು ನಿಮ್ಮ ಸಂಪರ್ಕಗಳು ಮತ್ತು ಕರೆ ಇತಿಹಾಸಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ಅನುಮತಿಸುತ್ತದೆ."</string>
@@ -333,7 +322,7 @@
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi ಲಾಗಿಂಗ್ ಮಟ್ಟನ್ನು ಹೆಚ್ಚಿಸಿ, Wi‑Fi ಆಯ್ಕೆಯಲ್ಲಿ ಪ್ರತಿಯೊಂದು SSID RSSI ತೋರಿಸಿ"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ಬ್ಯಾಟರಿ ಹೆಚ್ಚು ಬಾಳಿಕೆ ಬರುವಂತೆ ಮಾಡುತ್ತದೆ ಮತ್ತು ನೆಟ್ವರ್ಕ್ ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಸುಧಾರಿಸುತ್ತದೆ"</string>
<string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"ಈ ಮೋಡ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದಾಗ, MAC ಯಾದೃಚ್ಛಿಕರಣವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದ ನೆಟ್ವರ್ಕ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿದಾಗ ಈ ಸಾಧನದ MAC ವಿಳಾಸವು ಪ್ರತಿ ಬಾರಿಯೂ ಬದಲಾಗಬಹುದು."</string>
- <string name="wifi_metered_label" msgid="8737187690304098638">"ಮೀಟರ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="wifi_metered_label" msgid="8737187690304098638">"ಮೀಟರ್ಡ್ ನೆಟ್ವರ್ಕ್"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"ಮೀಟರ್ ಮಾಡಲಾಗಿಲ್ಲ"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ಲಾಗರ್ ಬಫರ್ ಗಾತ್ರಗಳು"</string>
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್ಗೆ ಲಾಗರ್ ಗಾತ್ರಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
@@ -487,7 +476,7 @@
<string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"ನಿಮ್ಮ ಬಳಕೆ ಆಧರಿಸಿ <xliff:g id="TIME">%1$s</xliff:g> ವರೆಗೆ ರನ್ ಆಗಲಿದೆ"</string>
<string name="power_discharge_by" msgid="4113180890060388350">"<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) ಸಮಯದವರೆಗೆ ರನ್ ಆಗಲಿದೆ"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"<xliff:g id="TIME">%1$s</xliff:g> ಸಮಯದವರೆಗೆ ರನ್ ಆಗಲಿದೆ"</string>
- <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> ರವರೆಗೆ"</string>
+ <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> ವರೆಗೆ ಇರುತ್ತದೆ"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"<xliff:g id="TIME">%1$s</xliff:g> ಗಳಲ್ಲಿ ಬ್ಯಾಟರಿ ಮುಕ್ತಾಯವಾಗಬಹುದು"</string>
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ಕ್ಕಿಂತ ಕಡಿಮೆ ಸಮಯ ಉಳಿದಿದೆ"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ಕ್ಕಿಂತ ಕಡಿಮೆ ಸಮಯ ಉಳಿದಿದೆ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ತಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ಅಪರಿಚಿತ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"ಪೂರ್ಣವಾಗಿ ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಹೋಲ್ಡ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ನಿರ್ವಾಹಕರ ಮೂಲಕ ನಿಯಂತ್ರಿಸಲಾಗಿದೆ"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ನಿರ್ಬಂಧಿಸಲಾದ ಸೆಟ್ಟಿಂಗ್ ಮೂಲಕ ನಿಯಂತ್ರಿಸಲಾಗುತ್ತದೆ"</string>
<string name="disabled" msgid="8017887509554714950">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
@@ -537,7 +538,7 @@
<string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"ಸಿಸ್ಟಂ ಭಾಷೆಗಳನ್ನು ಬಳಸಿ"</string>
<string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> ಗಾಗಿ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಲು ವಿಫಲವಾಗಿದೆ"</string>
<string name="ime_security_warning" msgid="6547562217880551450">"ವೈಯಕ್ತಿಕ ಡೇಟಾಗಳಾದ ಪಾಸ್ವರ್ಡ್ಗಳು ಮತ್ತು ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಸಂಖ್ಯೆಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ನೀವು ಟೈಪ್ ಮಾಡುವ ಎಲ್ಲ ಪಠ್ಯವನ್ನು ಸಂಗ್ರಹಿಸಲು ಈ ಇನ್ಪುಟ್ ವಿಧಾನಕ್ಕೆ ಸಾಧ್ಯವಾಗಬಹುದು. ಇದು <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ಅಪ್ಲಿಕೇಶನ್ನಿಂದ ಬರುತ್ತದೆ. ಈ ಇನ್ಪುಟ್ ವಿಧಾನವನ್ನು ಬಳಸುವುದೇ?"</string>
- <string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"ಗಮನಿಸಿ: ರೀಬೂಟ್ ನಂತರ, ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ನೀವು ಅನ್ಲಾಕ್ ಮಾಡುವ ತನಕ ಈ ಆಪ್ ಪ್ರಾರಂಭಗೊಳ್ಳುವುದಿಲ್ಲ"</string>
+ <string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"ಗಮನಿಸಿ: ರೀಬೂಟ್ ನಂತರ, ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ನೀವು ಅನ್ಲಾಕ್ ಮಾಡುವ ತನಕ ಈ ಆ್ಯಪ್ ಪ್ರಾರಂಭಗೊಳ್ಳುವುದಿಲ್ಲ"</string>
<string name="ims_reg_title" msgid="8197592958123671062">"IMS ನೋಂದಣಿ ಸ್ಥಿತಿ"</string>
<string name="ims_reg_status_registered" msgid="884916398194885457">"ನೋಂದಾಯಿಸಲಾಗಿದೆ"</string>
<string name="ims_reg_status_not_registered" msgid="2989287366045704694">"ನೋಂದಾಯಿಸಲಾಗಿಲ್ಲ"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"ಪೋನ್ ಒಂದು ಪಟ್ಟಿ."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"ಫೋನ್ ಎರಡು ಪಟ್ಟಿಗಳು."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"ಫೋನ್ ಮೂರು ಪಟ್ಟಿಗಳು."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"ಫೋನ್ ಸಂಕೇತ ಪೂರ್ತಿ ಇದೆ."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"ಯಾವುದೇ ಡೇಟಾ ಇಲ್ಲ."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"ಡೇಟಾ ಒಂದು ಪಟ್ಟಿ."</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index f193fef..e0331f5 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"활성, 왼쪽만"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"활성, 오른쪽만"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"활성, 왼쪽 및 오른쪽"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"사용 중(미디어 전용), 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"사용 중(미디어 전용), 왼쪽: 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, 오른쪽: 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"연결됨(오디오 공유 지원), 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"연결됨(오디오 공유 지원), 왼쪽: 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, 오른쪽: 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"연결됨(오디오 공유 지원), 왼쪽 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"연결됨(오디오 공유 지원), 오른쪽 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"사용 중(미디어 전용)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"오디오 공유 지원"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"사용 중(미디어 전용), 왼쪽만"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"사용 중(미디어 전용), 오른쪽만"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"사용 중(미디어 전용), 왼쪽 및 오른쪽"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"미디어 오디오"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"전화 통화"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"파일 전송"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 최적화됨"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ 충전 중"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"알 수 없음"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"충전 중"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"고속 충전 중"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"충전됨"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"완전히 충전됨"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"충전 일시중지"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"관리자가 제어"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"제한된 설정으로 제어됨"</string>
<string name="disabled" msgid="8017887509554714950">"사용 안함"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"휴대전화 신호 막대가 하나입니다."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"휴대전화 신호 막대가 두 개입니다."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"휴대전화 신호 막대가 세 개입니다."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"휴대전화의 신호가 강합니다."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"데이터가 없습니다."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"데이터 신호 막대가 하나입니다."</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 86426fa..cbc8be1 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Иштеп жатат, сол кулак гана"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Жигердүү, оң кулакчын гана"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Жигердүү, сол жана оң кулакчын"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Активдүү (медиа үчүн гана), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батарея"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Активдүү (медиа үчүн гана), С: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> батарея, О: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батарея"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Туташкан (чогуу угуу колдоого алынат), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батарея"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Туташкан (чогуу угуу колдоого алынат), С: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> батарея, О: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батарея"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Туташкан (чогуу угуу колдоого алынат), сол <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Туташкан (чогуу угуу колдоого алынат), оң <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Активдүү (медиа үчүн гана)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Чогуу угуу колдоого алынат"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Активдүү (медиа үчүн гана), сол кулакчын гана"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Активдүү (медиа үчүн гана), оң кулакчын гана"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Активдүү (медиа үчүн гана), сол жана оң кулакчын"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Аудио"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Телефон чалуулар"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл алмашуу"</string>
@@ -292,7 +281,7 @@
<string name="oem_unlock_enable" msgid="5334869171871566731">"OEM бөгөттөн чыгаруу"</string>
<string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Кайра жүктөгүчтү бөгөттөн чыгарууга уруксат берүү"</string>
<string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"OEM бөгөттөн чыгарууга уруксатпы?"</string>
- <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"ЭСКЕРТҮҮ: Бул жөндөө күйгүзүлүп турганда түзмөктү коргоо өзгөчөлүктөрү иштебейт."</string>
+ <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"ЭСКЕРТҮҮ: Бул параметр күйгүзүлүп турганда түзмөктү коргоо өзгөчөлүктөрү иштебейт."</string>
<string name="mock_location_app" msgid="6269380172542248304">"Жалган жайгашкан жерлерди көрсөткөн колдонмону тандоо"</string>
<string name="mock_location_app_not_set" msgid="6972032787262831155">"Жалган жайгашкан жерлерди көрсөткөн колдонмо жок"</string>
<string name="mock_location_app_set" msgid="4706722469342913843">"Жалган жайгашкан жерлерди көрсөткөн колдонмо: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -339,7 +328,7 @@
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Каттоо буфери үчүн Каттагычтын көлөмүн тандаңыз"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"Таржымалдын туруктуу диски тазалансынбы?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"Туруктуу таржымалга көз салууну токтотсок, анын түзмөктө сакталган дайындарын жок кылууга аргасыз болобуз."</string>
- <string name="select_logpersist_title" msgid="447071974007104196">"Журналдагы маалымат түзмөккө сакталсын"</string>
+ <string name="select_logpersist_title" msgid="447071974007104196">"Журналдагы нерселерди түзмөккө сактоо"</string>
<string name="select_logpersist_dialog_title" msgid="7745193591195485594">"Түзмөккө туруктуу сактоо үчүн таржымал буферлерин тандаңыз"</string>
<string name="select_usb_configuration_title" msgid="6339801314922294586">"USB конфигурациясын тандоо"</string>
<string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"USB конфигурациясын тандоо"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталат"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> — Кубаттоо жакшыртылды"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Кубатталууда"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Белгисиз"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Кубатталууда"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Кубатталды"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Толук кубатталды"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Кубаттоо күтүү режиминде"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Администратор тарабынан көзөмөлдөнөт"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Чектелген параметр аркылуу көзөмөлдөнөт"</string>
<string name="disabled" msgid="8017887509554714950">"Өчүрүлгөн"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Телефон сигналы бир таякча."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Телефон сигналы эки таякча."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Телефон сигналы үч таякча."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Телефон сигналы толук."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Сигнал жок."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Мобилдик интернеттин сигналы бир таякча."</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index ad1529d..2366c13 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"ນຳໃຊ້ຢູ່, ຊ້າຍເທົ່ານັ້ນ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"ນຳໃຊ້ຢູ່, ຂວາເທົ່ານັ້ນ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"ນຳໃຊ້ຢູ່, ຊ້າຍ ແລະ ຂວາ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"ນຳໃຊ້ຢູ່ (ມີເດຍເທົ່ານັ້ນ), ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"ນຳໃຊ້ຢູ່ (ມີເດຍເທົ່ານັ້ນ), ຊ: ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ຂ: ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"ເຊື່ອມຕໍ່ແລ້ວ (ຮອງຮັບການແບ່ງປັນສຽງ), ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"ເຊື່ອມຕໍ່ແລ້ວ (ຮອງຮັບການແບ່ງປັນສຽງ), ຊ: ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ຂ: ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"ເຊື່ອມຕໍ່ແລ້ວ (ຮອງຮັບການແບ່ງປັນສຽງ), ຊ້າຍ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"ເຊື່ອມຕໍ່ແລ້ວ (ຮອງຮັບການແບ່ງປັນສຽງ), ຂວາ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"ນຳໃຊ້ຢູ່ (ມີເດຍເທົ່ານັ້ນ)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ຮອງຮັບການແບ່ງປັນສຽງ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"ນຳໃຊ້ຢູ່ (ມີເດຍເທົ່ານັ້ນ), ຊ້າຍເທົ່ານັ້ນ"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"ນຳໃຊ້ຢູ່ (ມີເດຍເທົ່ານັ້ນ), ຂວາເທົ່ານັ້ນ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"ນຳໃຊ້ຢູ່ (ມີເດຍເທົ່ານັ້ນ), ຊ້າຍ ແລະ ຂວາ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"ສຽງ"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ການໂທ"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ການໂອນຍ້າຍໄຟລ໌"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"ຍັງເຫຼືອອີກ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະສາກເຕັມ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ການສາກຖືກປັບໃຫ້ເໝາະສົມແລ້ວ"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ກຳລັງສາກໄຟ"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ບໍ່ຮູ້ຈັກ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ກຳລັງສາກໄຟ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ກຳລັງສາກໄຟດ່ວນ"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"ສາກເຕັມແລ້ວ"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"ສາກເຕັມແລ້ວ"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ຢຸດການສາກຊົ່ວຄາວ"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ຄວບຄຸມໂດຍຜູ້ເບິ່ງແຍງ"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ຄວບຄຸມໂດຍການຕັ້ງຄ່າທີ່ຈຳກັດໄວ້"</string>
<string name="disabled" msgid="8017887509554714950">"ປິດການນຳໃຊ້"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"ສັນຍານນຶ່ງຂີດ."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"ສັນຍານສອງຂີດ."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"ສັນຍານສາມຂີດ."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"ສັນຍານເຕັມ."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"ບໍ່ມີຂໍ້ມູນ."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"ຂໍ້ມູນນຶ່ງຂີດ."</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 10ef2c4..479c850 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktyvus, tik kairysis"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktyvus, tik dešinysis"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktyvus, kairysis ir dešinysis"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktyvus (tik medija), akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktyvus (tik medija), kairė: akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, dešinė: akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Prijungta (palaikomas garso įrašų bendrinimas), akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Prijungta (palaikomas garso įrašų bendrinimas), kairė: akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, dešinė: akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Prijungta (palaikomas garso įrašų bendrinimas), kairė: akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Prijungta (palaikomas garso įrašų bendrinimas), kairė: akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktyvus (tik medija)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Palaikomas garso įrašų bendrinimas"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktyvus (tik medija), tik kairė"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktyvus (tik medija), tik dešinė"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktyvus (tik medija), kairė ir dešinė"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Medijos garsas"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefono skambučiai"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failo perkėlimas"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko <xliff:g id="TIME">%2$s</xliff:g>, kol bus visiškai įkrauta"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkrovimas optimizuotas"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkraunama"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nežinomas"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Kraunasi..."</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Greitai įkraunama"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Įkrauta"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Visiškai įkrautas"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Įkrovimas pristabdytas"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Valdo administratorius"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Valdoma pagal apribotą nustatymą"</string>
<string name="disabled" msgid="8017887509554714950">"Neleidžiama"</string>
@@ -555,7 +556,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Signalai ir priminimai"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Leisti nustatyti signalus ir priminimus"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Signalai ir priminimai"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Leisti šiai programai nustatyti signalus ir suplanuoti veiksmus, kuriems svarbus laiko veiksnys. Dėl to programa gali veikti fone ir sunaudoti daugiau akumuliatoriaus energijos.\n\nJei šis leidimas išjungtas, šios programos suplanuoti esami signalai ir laiku pagrįsti įvykiai neveiks."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Leiskite šiai programai nustatyti signalus ir suplanuoti veiksmus, kuriems svarbus laiko veiksnys. Dėl to programa gali veikti fone ir sunaudoti daugiau akumuliatoriaus energijos.\n\nJei šis leidimas išjungtas, šios programos suplanuoti esami signalai ir laiku pagrįsti įvykiai neveiks."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"tvarkaraštis, signalas, priminimas, laikrodis"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Įjungti"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Netrukdymo režimo įjungimas"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Viena telefono juosta."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Dvi telefono juostos."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Trys telefono juostos."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefono signalas stiprus."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Duomenų nėra."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Viena duomenų juosta."</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index c52e9dd..f4fc898 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Ierīce aktīva, tikai kreisā auss"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Ierīce aktīva, tikai labā auss"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Ierīces aktīvas, kreisā un labā auss"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktīvs (tikai multividei), akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktīvs (tikai multividei), labās austiņas akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, kreisās austiņas akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Izveidots savienojums (atbalsta audio kopīgošanu), akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Izveidots savienojums (atbalsta audio kopīgošanu), labās austiņas akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, kreisās austiņas akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Izveidots savienojums (atbalsta audio kopīgošanu), kreisās austiņas akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Izveidots savienojums (atbalsta audio kopīgošanu), labās austiņas akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktīvs (tikai multividei)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Atbalsta audio kopīgošanu"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktīvs (tikai multivide), tikai kreisās puses aparāts"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktīvs (tikai multivide), tikai labās puses aparāts"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktīvs (tikai multivide), kreisās un labās puses aparāts"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Multivides audio"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Tālruņa zvani"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failu pārsūtīšana"</string>
@@ -162,7 +151,7 @@
<string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Izmantot ievadei"</string>
<string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Izmantot dzirdes aparātiem"</string>
<string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Izmantot LE_AUDIO profilam"</string>
- <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Izveidot pāri"</string>
+ <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Savienot pārī"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SAVIENOT PĀRĪ"</string>
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Atcelt"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"Veicot savienošanu pārī, šī ierīce savienojuma laikā varēs piekļūt jūsu kontaktpersonām un zvanu vēsturei."</string>
@@ -487,7 +476,7 @@
<string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"Ņemot vērā lietojumu, darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="4113180890060388350">"Darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"Darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="5883041507426914446">"līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Iespējams, akumulators izlādēsies līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Atlicis: mazāk nekā <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"Atlicis: mazāk nekā <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> — uzlāde optimizēta"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> — notiek uzlāde"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nezināms"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Uzlāde"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Notiek ātrā uzlāde"</string>
@@ -509,12 +506,16 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Uzlādēts"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Pilnībā uzlādēts"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Uzlāde apturēta"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolē administrators"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolē ierobežots iestatījums"</string>
<string name="disabled" msgid="8017887509554714950">"Atspējots"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Atļauts"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Nav atļauts"</string>
- <string name="install_other_apps" msgid="3232595082023199454">"Instalēt nez. lietotnes"</string>
+ <string name="install_other_apps" msgid="3232595082023199454">"Nezināmu lietotņu instalēšana"</string>
<string name="home" msgid="973834627243661438">"Iestatījumu sākumekrāns"</string>
<string-array name="battery_labels">
<item msgid="7878690469765357158">"0%"</item>
@@ -541,7 +542,7 @@
<string name="ims_reg_title" msgid="8197592958123671062">"IMS reģistrācijas statuss"</string>
<string name="ims_reg_status_registered" msgid="884916398194885457">"Reģistrēts"</string>
<string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Nav reģistrēts"</string>
- <string name="status_unavailable" msgid="5279036186589861608">"Nepieejams"</string>
+ <string name="status_unavailable" msgid="5279036186589861608">"Nepieejama"</string>
<string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC ir atlasīts nejaušā secībā"</string>
<string name="wifi_tether_connected_summary" msgid="5100712926640492336">"{count,plural, =1{Pievienota 1 ierīce.}zero{Pievienotas # ierīces.}one{Pievienota # ierīce.}other{Pievienotas # ierīces.}}"</string>
<string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Vairāk laika."</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Tālrunis: viena josla."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Tālrunis: divas joslas."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Tālrunis: trīs joslas."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Pilna piekļuve tālruņa signālam"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Nav datu."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Dati: viena josla"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 554b92e..f5ff438 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -96,7 +96,7 @@
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (без телефон и аудиовизуелни содржини), ниво на батеријата <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Активен, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батерија"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Активен, Л: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> батерија, Д: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батерија"</string>
- <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батерија"</string>
+ <string name="bluetooth_battery_level" msgid="2893696778200201555">"Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Л: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> батерија, Д: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батерија"</string>
<string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"Одлево: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Активно, само лево"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Активно, само десно"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Активно, лево и десно"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Активно (само аудиовизуелни содржини), батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Активно (само аудиовизуелни содржини), батерија Л: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, батерија Д: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Поврзано (поддржува споделување аудио), батерија:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Поврзано (поддржува споделување аудио), батерија Л: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, батерија Д: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Поврзано (поддржува споделување аудио), лево: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Поврзано (поддржува споделување аудио), десно: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Активно (само аудиовизуелни содржини)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Поддржува споделување аудио"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Активно (само аудиовизуелни содржини), само лево"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Активно (само аудиовизуелни содржини), само десно"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Активно (само аудиовизуелни содржини), лево и десно"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Звук на аудио/видео"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Телефонски повици"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Пренос на датотека"</string>
@@ -201,7 +190,7 @@
<string name="running_process_item_user_label" msgid="3988506293099805796">"Корисник: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"Поставени се некои стандардни вредности"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"Нема поставено стандардни вредности"</string>
- <string name="tts_settings" msgid="8130616705989351312">"Поставки на текст-во-говор"</string>
+ <string name="tts_settings" msgid="8130616705989351312">"Поставки за „Од текст во говор“"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"Претворање текст во говор"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"Брзина на говорот"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"Брзина со која се кажува текстот"</string>
@@ -474,9 +463,9 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Корекција на боите"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Корекцијата на боите може да биде корисна кога сакате:<br/> <ol> <li>&nbsp;да ги гледате боите попрецизно</li> <li>&nbsp;да ги отстраните боите за полесно да се концентрирате</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Прескокнато според <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
- <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - полнењето е паузирано за да се заштити батеријата"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Проверете го додатокот за полнење"</string>
+ <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
+ <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – полнењето е паузирано за да се заштити батеријата"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Проверете го додатокот за полнење"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g> според вашето користење"</string>
@@ -496,8 +485,16 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до полна батерија"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полна батерија"</string>
- <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Полнењето е оптимизирано"</string>
- <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ се полни"</string>
+ <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – полнењето е оптимизирано"</string>
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – се полни"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Се полни"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо полнење"</string>
@@ -509,12 +506,16 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Полна"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Целосно полна"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Полнењето е паузирано"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Контролирано од администраторот"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролирано со ограничени поставки"</string>
<string name="disabled" msgid="8017887509554714950">"Оневозможено"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Со дозвола"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Без дозвола"</string>
- <string name="install_other_apps" msgid="3232595082023199454">"Непознати апликации"</string>
+ <string name="install_other_apps" msgid="3232595082023199454">"Инсталирање непознати апликации"</string>
<string name="home" msgid="973834627243661438">"Почетна страница за поставки"</string>
<string-array name="battery_labels">
<item msgid="7878690469765357158">"0 %"</item>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Телефон една цртичка.."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Телефон две цртички."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Телефон три цртички."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Сигналот за телефон е исполнет."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Нема податоци."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Податоци една цртичка."</string>
@@ -709,7 +712,7 @@
<string name="keyboard_layout_default_label" msgid="1997292217218546957">"Стандардно"</string>
<string name="turn_screen_on_title" msgid="3266937298097573424">"Вклучување на екранот"</string>
<string name="allow_turn_screen_on" msgid="6194845766392742639">"Дозволи вклучување на екранот"</string>
- <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Дозволува одредена апликација да го вклучува екранот. Ако е дозволено, апликацијата може да го вклучува екранот во секое време без ваша намера."</string>
+ <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Дозволете одредена апликација да го вклучува екранот. Ако е дозволено, апликацијата ќе може да го вклучува екранот во секое време без ваша намера."</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Да се прекине емитувањето на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Ако емитувате на <xliff:g id="SWITCHAPP">%1$s</xliff:g> или го промените излезот, тековното емитување ќе запре"</string>
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Емитување на <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 61bcc44..40cf60e 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"സജീവമാണ്, ഇടത്തേത് മാത്രം"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"സജീവമാണ്, വലത്തേത് മാത്രം"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"സജീവമാണ്, ഇടത്തേതും വലത്തേതും"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"സജീവം (മീഡിയ മാത്രം), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ബാറ്ററി"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"സജീവം (മീഡിയ മാത്രം), ഇടതുവശത്ത്: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ബാറ്ററി, വലതുവശത്ത്: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ബാറ്ററി"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"കണക്റ്റ് ചെയ്തു (ഓഡിയോ പങ്കിടൽ പിന്തുണയ്ക്കുന്നു), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ബാറ്ററി"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"കണക്റ്റ് ചെയ്തു (ഓഡിയോ പങ്കിടൽ പിന്തുണയ്ക്കുന്നു), ഇടതുവശത്ത്: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ബാറ്ററി, വലതുവശത്ത്: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ബാറ്ററി"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"കണക്റ്റ് ചെയ്തു (ഓഡിയോ പങ്കിടൽ പിന്തുണയ്ക്കുന്നു), ഇടതുവശത്ത് <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ബാറ്ററി"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"കണക്റ്റ് ചെയ്തു (ഓഡിയോ പങ്കിടൽ പിന്തുണയ്ക്കുന്നു), വലതുവശത്ത് <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ബാറ്ററി"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"സജീവം (മീഡിയ മാത്രം)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ഓഡിയോ പങ്കിടൽ പിന്തുണയ്ക്കുന്നു"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"സജീവം (മീഡിയ മാത്രം), ഇടതുവശത്ത് മാത്രം"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"സജീവം (മീഡിയ മാത്രം), വലതുവശത്ത് മാത്രം"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"സജീവം (മീഡിയ മാത്രം), ഇടതുവശത്തെയും വലതുവശത്തെയും"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"മീഡിയ ഓഡിയോ"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ഫോണ് കോളുകൾ"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ഫയൽ കൈമാറൽ"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമാകാൻ <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ചാർജിംഗ് ഒപ്റ്റിമൈസ് ചെയ്തു"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ ചാർജ് ചെയ്യുന്നു"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"അജ്ഞാതം"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ചാർജ് ചെയ്യുന്നു"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"അതിവേഗ ചാർജിംഗ്"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"ചാർജായി"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"പൂർണ്ണമായി ചാർജ് ചെയ്തു"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ചാർജിംഗ് ഹോൾഡിലാണ്"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"അഡ്മിൻ നിയന്ത്രിക്കുന്നത്"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"നിയന്ത്രിത ക്രമീകരണം ഉപയോഗിച്ച് നിയന്ത്രിക്കുന്നത്"</string>
<string name="disabled" msgid="8017887509554714950">"പ്രവർത്തനരഹിതമാക്കി"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"ഫോണിൽ ഒരു ബാർ."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"ഫോണിൽ രണ്ട് ബാർ."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"ഫോണിൽ മൂന്ന് ബാർ."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"ഫോൺ സിഗ്നൽ പൂർണ്ണമാണ്."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"ഡാറ്റാ സിഗ്നൽ ഒന്നുമില്ല."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"ഡാറ്റ ഒരു ബാർ."</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index c2c22e6..f4b82ea 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Идэвхтэй, зөвхөн зүүн тал"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Идэвхтэй, зөвхөн баруун тал"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Идэвхтэй, зүүн болон баруун тал"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Идэвхтэй (зөвхөн медиа), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батарей"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Идэвхтэй (зөвхөн медиа), З: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> батарей, Б: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батарей"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Холбогдсон (аудио хуваалцахыг дэмждэг), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батарей"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Холбогдсон (аудио хуваалцахыг дэмждэг), З: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> батарей, Б: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батарей"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Холбогдсон (аудио хуваалцахыг дэмждэг), зүүн <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Холбогдсон (аудио хуваалцахыг дэмждэг), баруун <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Идэвхтэй (зөвхөн медиа)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Аудио хуваалцахыг дэмждэг"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Идэвхтэй (зөвхөн медиа), зөвхөн зүүн"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Идэвхтэй (зөвхөн медиа), зөвхөн баруун"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Идэвхтэй (зөвхөн медиа), зүүн болон баруун"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Медиа аудио"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Утасны дуудлага"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл дамжуулалт"</string>
@@ -472,7 +461,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномаль (улаан-ногоон)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомаль (цэнхэр-шар)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Өнгө тохируулга"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Өнгө тохируулга нь таныг дараахыг хийхийг хүсэх үед хэрэгтэй байж болно:<br/> <ol> <li>&nbsp;Өнгөнүүдийг илүү нарийвчилж харах</li> <li>&nbsp;Төвлөрөхийн тулд өнгөнүүдийг хасах</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Өнгө засах нь таныг дараахыг хийхийг хүсэх үед хэрэгтэй байж болно:<br/> <ol> <li> Өнгөнүүдийг илүү нарийвчилж харах</li> <li> Төвлөрөхийн тулд өнгөнүүдийг хасах</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейг хамгаалахын тулд цэнэглэхийг хүлээлгэсэн"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - дүүрэх хүртэл <xliff:g id="TIME">%2$s</xliff:g> үлдсэн"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэх явцыг оновчилсон"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэж байна"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Тодорхойгүй"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Цэнэглэж байна"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Цэнэглэсэн"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Бүрэн цэнэглэсэн"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Цэнэглэхийг хүлээлгэд оруулсан"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Админ удирдсан"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Хязгаарлагдсан тохиргоогоор хянадаг"</string>
<string name="disabled" msgid="8017887509554714950">"Идэвхгүйжүүлсэн"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Утас нэг баганатай."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Утас хоёр баганатай."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Утас гурван баганатай."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Утасны дохио дүүрэн."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Дата байхгүй."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Дата нэг баганатай."</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 1e56118..7ea7579 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"फक्त डावे अॅक्टिव्ह आहे"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"फक्त उजवे अॅक्टिव्ह आहे"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"डावे आणि उजवे अॅक्टिव्ह आहे"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"अॅक्टिव्ह आहे (फक्त मीडिया), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> बॅटरी"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"ॲक्टिव्ह आहे (फक्त मीडिया), डावे: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> बॅटरी, उजवे: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> बॅटरी"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"कनेक्ट केले आहे (ऑडिओ शेअरिंगला सपोर्ट करते), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> बॅटरी"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"कनेक्ट केले आहे (ऑडिओ शेअरिंगला सपोर्ट करते), डावे: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> बॅटरी, उजवे: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> बॅटरी"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"कनेक्ट केले आहे (ऑडिओ शेअरिंगला सपोर्ट करते), डावे <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"कनेक्ट केले आहे (ऑडिओ शेअरिंगला सपोर्ट करते), उजवे <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"अॅक्टिव्ह आहे (फक्त मीडिया)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ऑडिओ शेअरिंगला सपोर्ट करते"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"अॅक्टिव्ह आहे (फक्त मीडिया), फक्त डावे"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"अॅक्टिव्ह आहे (फक्त मीडिया), फक्त उजवे"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"अॅक्टिव्ह आहे (फक्त मीडिया), डावे आणि उजवे"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"मीडिया ऑडिओ"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"फोन कॉल"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"फाइल स्थानांतरण"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूर्ण चार्ज होण्यासाठी <xliff:g id="TIME">%2$s</xliff:g> शिल्लक आहे"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग ऑप्टिमाइझ केले"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ चार्ज होत आहे"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज होत आहे"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"वेगाने चार्ज होत आहे"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"चार्ज झाली"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"पूर्ण चार्ज झाली"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"चार्जिंग थांबवले आहे"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"प्रशासकाने नियंत्रित केलेले"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"प्रतिबंधित केलेल्या सेटिंग द्वारे नियंत्रित"</string>
<string name="disabled" msgid="8017887509554714950">"अक्षम"</string>
@@ -537,7 +538,7 @@
<string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"सिस्टम भाषा वापरा"</string>
<string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> साठी सेटिंग्ज उघडण्यात अयशस्वी"</string>
<string name="ime_security_warning" msgid="6547562217880551450">"ही इनपुट पद्धत पासवर्ड आणि क्रेडिट कार्ड नंबर यासह, तुम्ही टाइप करता तो सर्व मजकूर संकलित करण्यात सक्षम होऊ शकते. ही <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ॲपवरून येते. ही इनपुट पद्धत वापरायची?"</string>
- <string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"टीप: रीबूट केल्यानंतर, तुम्ही तुमचा फोन अनलॉक करे पर्यंत हे अॅप सुरू होऊ शकत नाही"</string>
+ <string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"टीप: रीबूट केल्यानंतर, तुम्ही तुमचा फोन अनलॉक करेपर्यंत हे अॅप सुरू होऊ शकत नाही"</string>
<string name="ims_reg_title" msgid="8197592958123671062">"IMS नोंदणी स्थिती"</string>
<string name="ims_reg_status_registered" msgid="884916398194885457">"नोंदवलेले"</string>
<string name="ims_reg_status_not_registered" msgid="2989287366045704694">"नोंदवलेले नाही"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"फोन एक बार."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"फोन दोन बार."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"फोन तीन बार."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"फोन सिग्नल पूर्ण."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"कोणताही डेटा नाही."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"डेटा एक बार."</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index a93c459..a64aff3 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktif, kiri sahaja"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktif, kanan sahaja"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktif, kiri dan kanan"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktif (media sahaja), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> bateri"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktif (media sahaja), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> bateri, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> bateri"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Disambungkan (menyokong perkongsian audio), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> bateri"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Disambungkan (menyokong perkongsian audio), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> bateri, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> bateri"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Disambungkan (menyokong perkongsian audio), kiri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Disambungkan (menyokong perkongsian audio), kanan <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktif (media sahaja)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Menyokong perkongsian audio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktif (media sahaja), kiri sahaja"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktif (media sahaja), kanan sahaja"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktif (media sahaja), kiri dan kanan"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Audio media"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Panggilan telefon"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Pemindahan fail"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi sebelum penuh"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengecasan dioptimumkan"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Mengecas"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Mengecas"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengecas pantas"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Sudah dicas"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Dicas Penuh"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Pengecasan ditunda"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Dikawal oleh pentadbir"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Dikawal oleh Tetapan Terhad"</string>
<string name="disabled" msgid="8017887509554714950">"Dilumpuhkan"</string>
@@ -555,7 +556,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Penggera dan peringatan"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Benarkan penetapan penggera dan peringatan"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Penggera & peringatan"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Benarkan apl ini menetapkan penggera dan menjadualkan tindakan yang sensitif masa. Ini membolehkan apl berjalan di latar, yang mungkin menggunakan lebih banyak bateri.\n\nJika kebenaran ini dimatikan, penggera sedia ada dan acara berdasarkan masa yang dijadualkan oleh apl ini tidak akan berfungsi."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Benarkan apl ini menetapkan penggera dan menjadualkan tindakan yang sensitif masa. Hal ini membolehkan apl berjalan di latar, yang mungkin menggunakan lebih banyak bateri.\n\nJika kebenaran ini dimatikan, penggera sedia ada dan acara berdasarkan masa yang dijadualkan oleh apl ini tidak akan berfungsi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"jadual, penggera, peringatan, jam"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Hidupkan"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Hidupkan Jangan Ganggu"</string>
@@ -638,7 +639,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Gagal membuat pengguna baharu"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Gagal membuat tetamu baharu"</string>
<string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
- <string name="edit_user_info_message" msgid="6677556031419002895">"Nama dan gambar yang anda pilih akan dipaparkan kepada sesiapa sahaja yang menggunakan peranti ini."</string>
+ <string name="edit_user_info_message" msgid="6677556031419002895">"Nama dan gambar yang anda pilih dapat dilihat oleh sesiapa sahaja yang menggunakan peranti ini."</string>
<string name="user_add_user" msgid="7876449291500212468">"Tambah pengguna"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefon satu bar."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefon dua bar."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefon tiga bar."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Isyarat telefon penuh."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Tiada data."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data satu bar."</string>
@@ -708,7 +711,7 @@
<string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"Pilih susun atur papan kekunci"</string>
<string name="keyboard_layout_default_label" msgid="1997292217218546957">"Lalai"</string>
<string name="turn_screen_on_title" msgid="3266937298097573424">"Hidupkan skrin"</string>
- <string name="allow_turn_screen_on" msgid="6194845766392742639">"Benarkan menghidupkan skrin"</string>
+ <string name="allow_turn_screen_on" msgid="6194845766392742639">"Benarkan apl menghidupkan skrin"</string>
<string name="allow_turn_screen_on_description" msgid="43834403291575164">"Benarkan apl menghidupkan skrin. Jika dibenarkan, apl boleh menghidupkan skrin pada bila-bila masa tanpa niat eksplisit anda."</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Hentikan siaran <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Jika anda siarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g> atau tukarkan output, siaran semasa anda akan berhenti"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 7880c37..a94c359 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"ဖွင့်ထားသည်၊ ဘယ်သီးသန့်"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"ဖွင့်ထားသည်၊ ညာသီးသန့်"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"ဖွင့်ထားသည်၊ ဘယ်နှင့် ညာ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"သုံးနေသည် (မီဒီယာသီးသန့်)၊ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ဘက်ထရီ"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"သုံးနေသည် (မီဒီယာသီးသန့်)၊ L- <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ဘက်ထရီ၊ R- <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ဘက်ထရီ"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"ချိတ်ဆက်ထားသည် (အော်ဒီယို မျှဝေခြင်း ပံ့ပိုးသည်)၊ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ဘက်ထရီ"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"ချိတ်ဆက်ထားသည် (အော်ဒီယို မျှဝေခြင်း ပံ့ပိုးသည်)၊ L- <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ဘက်ထရီ၊ R- <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ဘက်ထရီ"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"ချိတ်ဆက်ထားသည် (အော်ဒီယို မျှဝေခြင်း ပံ့ပိုးသည်)၊ ဘယ် <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"ချိတ်ဆက်ထားသည် (အော်ဒီယို မျှဝေခြင်း ပံ့ပိုးသည်)၊ ညာ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"သုံးနေသည် (မီဒီယာသီးသန့်)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"အော်ဒီယို မျှဝေခြင်း ပံ့ပိုးသည်"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"သုံးနေသည် (မီဒီယာသီးသန့်)၊ ဘယ်သီးသန့်"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"သုံးနေသည် (မီဒီယာသီးသန့်)၊ ညာသီးသန့်"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"သုံးနေသည် (မီဒီယာသီးသန့်)၊ ဘယ်နှင့် ညာ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"မီဒီယာ အသံ"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ဖုန်းခေါ်ဆိုမှုများ"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ဖိုင်လွဲပြောင်းခြင်း"</string>
@@ -272,7 +261,7 @@
<string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"ချိတ်ဆက်ခြင်း မအောင်မြင်ပါ"</string>
<string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> သည် မှန်ကန်သည့် ကွန်ရက်သို့ ချိတ်ဆက်ထားခြင်းရှိမရှိ စစ်ဆေးပါ"</string>
<string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"စက်ပစ္စည်းနှင့် အတူတွဲပါ"</string>
- <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi အတူတွဲချိတ်ရန် ကုဒ်"</string>
+ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi တွဲချိတ်ကုဒ်"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"တွဲချိတ်ခြင်း မအောင်မြင်ပါ"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"စက်ပစ္စည်းသည် ကွန်ရက်တစ်ခုတည်းသို့ ချိတ်ဆက်ထားခြင်းရှိမရှိ စစ်ဆေးပါ။"</string>
<string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR ကုဒ် စကင်ဖတ်ခြင်းဖြင့် Wi-Fi ပေါ်တွင် စက်ပစ္စည်းကို အတူတွဲပါ"</string>
@@ -471,7 +460,7 @@
<string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"Deuteranomaly (အနီ-အစိမ်း)"</string>
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (အနီ-အစိမ်း)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (အပြာ-အဝါ)"</string>
- <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"အရောင်ပြင်ခြင်း"</string>
+ <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"အရောင် အမှန်ပြင်ခြင်း"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"အရောင် အမှန်ပြင်ခြင်းသည် အောက်ပါတို့အတွက် အသုံးဝင်နိုင်သည်-<br/> <ol> <li>&nbsp;အရောင်များကို ပိုမိုမှန်ကန်စွာ ကြည့်ရှုခြင်း</li> <li>&nbsp;အာရုံစိုက်နိုင်ရန် အရောင်များ ဖယ်ရှားခြင်း</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> လိုသည်"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းခြင်းကို အကောင်းဆုံးပြင်ဆင်ထားသည်"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းနေသည်"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"မသိ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"အားသွင်းနေပါသည်"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"အမြန် အားသွင်းနေသည်"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"အားသွင်းပြီးပါပြီ"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"အားအပြည့်သွင်းထားသည်"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"အားသွင်းခြင်းကို ခဏရပ်ထားသည်"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"စီမံခန့်ခွဲသူမှ ထိန်းချုပ်ပါသည်"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ကန့်သတ်ဆက်တင်ဖြင့် ထိန်းချုပ်ထားသည်"</string>
<string name="disabled" msgid="8017887509554714950">"ပိတ်ထားပြီး"</string>
@@ -555,7 +556,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"နှိုးစက်နှင့် သတိပေးချက်များ"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"နှိုးစက်နှင့် သတိပေးချက်များ သတ်မှတ်ခွင့်ပြုရန်"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"နှိုးစက်နှင့် သတိပေးချက်များ"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"နှိုးစက်သတ်မှတ်ရန်နှင့် အချိန်တိကျရန် လိုအပ်သည့် လုပ်ဆောင်ချက်များ အစီအစဉ်ဆွဲရန် ဤအက်ပ်ကို ခွင့်ပြုပါ။ ၎င်းက အက်ပ်ကို နောက်ခံတွင် လုပ်ဆောင်ခွင့်ပေးပြီး ဘက်ထရီပိုသုံးနိုင်သည်။\n\nဤခွင့်ပြုချက်ကို ပိတ်ထားပါက ဤအက်ပ်ဖြင့် အစီအစဉ်ဆွဲထားသော လက်ရှိနှိုးစက်နှင့် အချိန်သတ်မှတ်ထားသည့် အစီအစဉ်များ အလုပ်လုပ်တော့မည် မဟုတ်ပါ။"</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"နှိုးစက်သတ်မှတ်ရန်နှင့် အချိန်တိကျရန် လိုအပ်သည့် လုပ်ဆောင်ချက်များအတွက် အစီအစဉ်ဆွဲရန် ဤအက်ပ်ကို ခွင့်ပြုသည်။ ၎င်းက အက်ပ်ကို နောက်ခံတွင် လုပ်ဆောင်ခွင့်ပေးပြီး ဘက်ထရီပိုသုံးနိုင်သည်။\n\nဤခွင့်ပြုချက်ကို ပိတ်ထားပါက ဤအက်ပ်ဖြင့် အစီအစဉ်ဆွဲထားသော လက်ရှိနှိုးစက်နှင့် အချိန်သတ်မှတ်ထားသည့် အစီအစဉ်များ အလုပ်လုပ်တော့မည် မဟုတ်ပါ။"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"အချိန်ဇယား၊ နှိုးစက်၊ သတိပေးချက်၊ နာရီ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ဖွင့်ရန်"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'မနှောင့်ယှက်ရ\' ဖွင့်ခြင်း"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"ဖုန်းလိုင်းတစ်ဘား။"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"ဖုန်းလိုင်းနှစ်ဘား။"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"ဖုန်းလိုင်းသုံးဘား။"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"ဖုန်းလိုင်းအပြည့်။"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"ဒေတာမရှိပါ။"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"ဒေတာတစ်ဘား။"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 0eacc5e..3b73bc3 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiv, bare venstre"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiv, bare høyre"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiv, venstre og høyre"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktiv (bare medieinnhold), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktiv (bare medieinnhold), v: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri, h: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Tilkoblet (støtter lyddeling), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Tilkoblet (støtter lyddeling), v: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri, h: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Tilkoblet (støtter lyddeling), venstre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Tilkoblet (støtter lyddeling), høyre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktiv (bare medieinnhold)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Støtter lyddeling"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktiv (bare medieinnhold), bare venstre"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktiv (bare medieinnhold), bare høyre"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktiv (bare medieinnhold), høyre og venstre"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Medielyd"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonsamtaler"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filoverføring"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Fulladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladingen er optimalisert"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – lader"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ukjent"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Lader"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Ladet"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fulladet"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Ladingen er satt på vent"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrollert av administratoren"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrollert av en begrenset innstilling"</string>
<string name="disabled" msgid="8017887509554714950">"Slått av"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefon – én stolpe."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefon – to stolper."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefon – tre stolper."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefonsignal er fullt."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Ingen data."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data – én stolpe"</string>
diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml
index 447cd80..d2c3729 100644
--- a/packages/SettingsLib/res/values-ne/arrays.xml
+++ b/packages/SettingsLib/res/values-ne/arrays.xml
@@ -55,7 +55,7 @@
</string-array>
<string-array name="hdcp_checking_summaries">
<item msgid="4045840870658484038">"HDCP परीक्षण कहिल्यै प्रयोग नगर्नुहोस्"</item>
- <item msgid="8254225038262324761">"DRM सामग्रीको लागि मात्र HDCP जाँचको प्रयोग गरियोस्"</item>
+ <item msgid="8254225038262324761">"DRM सामग्रीको लागि मात्र HDCP जाँचको प्रयोग गर्नुहोस्"</item>
<item msgid="6421717003037072581">"सधैँ HDCP जाँच प्रयोग गर्नुहोस्"</item>
</string-array>
<string-array name="bt_hci_snoop_log_entries">
@@ -97,7 +97,7 @@
<item msgid="8147982633566548515">"map14"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
+ <item msgid="2494959071796102843">"सिस्टमको छनौट प्रयोग गर्नुहोस् (डिफल्ट)"</item>
<item msgid="4055460186095649420">"SBC"</item>
<item msgid="720249083677397051">"AAC"</item>
<item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> अडियो"</item>
@@ -107,7 +107,7 @@
<item msgid="506175145534048710">"Opus"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
+ <item msgid="8868109554557331312">"सिस्टमको छनौट प्रयोग गर्नुहोस् (डिफल्ट)"</item>
<item msgid="9024885861221697796">"SBC"</item>
<item msgid="4688890470703790013">"AAC"</item>
<item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> अडियो"</item>
@@ -117,38 +117,38 @@
<item msgid="7940970833006181407">"Opus"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
- <item msgid="926809261293414607">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
+ <item msgid="926809261293414607">"सिस्टमको छनौट प्रयोग गर्नुहोस् (डिफल्ट)"</item>
<item msgid="8003118270854840095">"४४.१ kHz"</item>
<item msgid="3208896645474529394">"४८.० kHz"</item>
<item msgid="8420261949134022577">"८८.२ kHz"</item>
<item msgid="8887519571067543785">"९६.० kHz"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
- <item msgid="2284090879080331090">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
+ <item msgid="2284090879080331090">"सिस्टमको छनौट प्रयोग गर्नुहोस् (डिफल्ट)"</item>
<item msgid="1872276250541651186">"४४.१ kHz"</item>
<item msgid="8736780630001704004">"४८.० kHz"</item>
<item msgid="7698585706868856888">"८८.२ kHz"</item>
<item msgid="8946330945963372966">"९६.० kHz"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
- <item msgid="2574107108483219051">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
+ <item msgid="2574107108483219051">"सिस्टमको छनौट प्रयोग गर्नुहोस् (डिफल्ट)"</item>
<item msgid="4671992321419011165">"१६ बिट/नमूना"</item>
<item msgid="1933898806184763940">"२४ बिट/नमूना"</item>
<item msgid="1212577207279552119">"३२ बिट/नमूना"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
- <item msgid="9196208128729063711">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
+ <item msgid="9196208128729063711">"सिस्टमको छनौट प्रयोग गर्नुहोस् (डिफल्ट)"</item>
<item msgid="1084497364516370912">"१६ बिट/नमूना"</item>
<item msgid="2077889391457961734">"२४ बिट/नमूना"</item>
<item msgid="3836844909491316925">"३२ बिट/नमूना"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_channel_mode_titles">
- <item msgid="3014194562841654656">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
+ <item msgid="3014194562841654656">"सिस्टमको छनौट प्रयोग गर्नुहोस् (डिफल्ट)"</item>
<item msgid="5982952342181788248">"मोनो"</item>
<item msgid="927546067692441494">"स्टेरियो"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
- <item msgid="1997302811102880485">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
+ <item msgid="1997302811102880485">"सिस्टमको छनौट प्रयोग गर्नुहोस् (डिफल्ट)"</item>
<item msgid="8005696114958453588">"मोनो"</item>
<item msgid="1333279807604675720">"स्टेरियो"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index e2236cc..c6a8706 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -106,41 +106,30 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"बायाँ मात्र अन छ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"सक्रिय, दायाँ मात्र"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"सक्रिय, बायाँ र दायाँ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"सक्रिय छ (मिडिया मात्र), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ब्याट्री"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"सक्रिय छ (मिडिया मात्र), बायाँ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ब्याट्री, दायाँ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ब्याट्री"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"कनेक्ट गरिएको छ (अडियो सेयर गर्न मिल्छ), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ब्याट्री"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"कनेक्ट गरिएको छ (अडियो सेयर गर्न मिल्छ), बायाँ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ब्याट्री, दायाँ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ब्याट्री"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"कनेक्ट गरिएको छ (अडियो सेयर गर्न मिल्छ), बायाँ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"कनेक्ट गरिएको छ (अडियो सेयर गर्न मिल्छ), दायाँ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"सक्रिय छ (मिडिया मात्र)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"अडियो सेयर गर्न मिल्छ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"सक्रिय छ (मिडिया मात्र), बायाँ मात्र"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"सक्रिय छ (मिडिया मात्र), दायाँ मात्र"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"सक्रिय छ (मिडिया मात्र), बायाँ र दायाँ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"मिडिया अडियो"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"फोन कलहरू"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"फाइल स्थानान्तरण"</string>
- <string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट उपकरण"</string>
+ <string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट डिभाइस"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"इन्टरनेट एक्सेस"</string>
- <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"कन्ट्याक्ट र कल हिस्ट्री एक्सेस गर्ने अनुमति दिइयोस्"</string>
+ <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>
<string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD अडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD अडियो"</string>
- <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"श्रवण यन्त्रहरू"</string>
+ <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"हियरिङ डिभाइसहरू"</string>
<string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE अडियो"</string>
<string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"श्रवण यन्त्रहरूमा जडान गरियो"</string>
<string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE अडियोमा कनेक्ट गरिएको छ"</string>
@@ -176,7 +165,7 @@
<string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"छवि सम्बन्धी"</string>
<string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"हेडफोन"</string>
<string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"इनपुट सम्बन्धी बाह्य यन्त्र"</string>
- <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"श्रवण यन्त्रहरू"</string>
+ <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"हियरिङ डिभाइसहरू"</string>
<string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"ब्लुटुथ"</string>
<string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi बन्द।"</string>
<string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi जडान विच्छेद भयो।"</string>
@@ -256,14 +245,14 @@
<string name="enable_adb_summary" msgid="3711526030096574316">"USB कनेक्ट गरिएको बेलामा डिबग मोड"</string>
<string name="clear_adb_keys" msgid="3010148733140369917">"USB डिबग गर्ने अधिकार फिर्ता लिइयोस्"</string>
<string name="enable_adb_wireless" msgid="6973226350963971018">"वायरलेस डिबगिङ"</string>
- <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Wi‑Fi मा कनेक्ट हुँदा डिबग मोड अन गरियोस्"</string>
+ <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Wi‑Fi मा कनेक्ट हुँदा डिबग मोड अन गर्नुहोस्"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"त्रुटि"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"वायरलेस डिबगिङ"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"उपलब्ध डिभाइस हेर्न र प्रयोग गर्न वायरलेस डिबगिङ अन गर्नुहोस्"</string>
- <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR कोड प्रयोग गरी डिभाइस कनेक्ट गरियोस्"</string>
+ <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR कोड प्रयोग गरी डिभाइस कनेक्ट गर्नुहोस्"</string>
<string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR कोड स्क्यानर प्रयोग गरी नयाँ डिभाइसहरूको जोडा बनाउनुहोस्"</string>
- <string name="adb_pair_method_code_title" msgid="1122590300445142904">"पेयरिङ कोड प्रयोग गरी कनेक्ट गरियोस्"</string>
- <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"छ अङ्कको कोड प्रयोग गरी नयाँ डिभाइसहरू कनेक्ट गरियोस्"</string>
+ <string name="adb_pair_method_code_title" msgid="1122590300445142904">"पेयरिङ कोड प्रयोग गरी कनेक्ट गर्नुहोस्"</string>
+ <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"छ अङ्कको कोड प्रयोग गरी नयाँ डिभाइसहरू कनेक्ट गर्नुहोस्"</string>
<string name="adb_paired_devices_title" msgid="5268997341526217362">"कनेक्ट गरिएका डिभाइस"</string>
<string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"हाल जोडिएको छ"</string>
<string name="adb_wireless_device_details_title" msgid="7129369670526565786">"डिभाइसको विवरण"</string>
@@ -284,7 +273,7 @@
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"कृपया कुनै Wi-Fi मा कनेक्ट गर्नुहोस्"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, debug, dev"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"बग रिपोर्टको सर्टकट"</string>
- <string name="bugreport_in_power_summary" msgid="1885529649381831775">"बग रिपोर्ट प्राप्त गर्न पावर मेनुमा बटन देखाइयोस्"</string>
+ <string name="bugreport_in_power_summary" msgid="1885529649381831775">"बग रिपोर्ट प्राप्त गर्न पावर मेनुमा बटन देखाउनुहोस्"</string>
<string name="keep_screen_on" msgid="1187161672348797558">"डिस्प्ले अफ नहोस्"</string>
<string name="keep_screen_on_summary" msgid="1510731514101925829">"चार्ज गर्दा स्क्रिन कहिल्यै अफ हुँदैन।"</string>
<string name="bt_hci_snoop_log" msgid="7291287955649081448">"ब्लुटुथ HCI snoop लग अन गर्नुहोस्"</string>
@@ -298,14 +287,14 @@
<string name="mock_location_app_set" msgid="4706722469342913843">"नमूना स्थान एप: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="debug_networking_category" msgid="6829757985772659599">"नेटवर्किङ"</string>
<string name="wifi_display_certification" msgid="1805579519992520381">"वायरलेस डिस्प्ले प्रयोग गर्ने वा नगर्ने"</string>
- <string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi-Fi भर्बोज लग अन गरियोस्"</string>
+ <string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi-Fi भर्बोज लग अन गर्नुहोस्"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi स्क्यान थ्रोटलिङ"</string>
<string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi-Fi नन-पर्सिस्टेन्ट MAC र्यान्डमाइजेसन"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"मोबाइल डेटा सधैँ अन होस्"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"टेदरिङको लागि हार्डवेयरको प्रवेग"</string>
- <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नामकरण नगरिएका ब्लुटुथ डिभाइस देखाइयोस्"</string>
- <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"निरपेक्ष भोल्युम अफ गरियोस्"</string>
- <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche अन गरियोस्"</string>
+ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नामकरण नगरिएका ब्लुटुथ डिभाइस देखाउनुहोस्"</string>
+ <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"निरपेक्ष भोल्युम अफ गर्नुहोस्"</string>
+ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche अन गर्नुहोस्"</string>
<string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लुटुथको AVRCP संस्करण"</string>
<string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लुटुथको AVRCP संस्करण चयन गर्नुहोस्"</string>
<string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लुटुथको MAP संस्करण"</string>
@@ -329,8 +318,8 @@
<string name="private_dns_mode_provider" msgid="3619040641762557028">"निजी DNS प्रदायकको होस्टनेम"</string>
<string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"DNS प्रदायकको होस्टनेम हाल्नुहोस्"</string>
<string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"जडान गर्न सकिएन"</string>
- <string name="wifi_display_certification_summary" msgid="8111151348106907513">"वायरलेस डिस्प्लेसम्बन्धी विकल्प देखाइयोस्"</string>
- <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi-Fi लगिङ लेभल बढाइयोस्, Wi-Fi पिकरमा प्रति SSID RSSI देखाइयोस्"</string>
+ <string name="wifi_display_certification_summary" msgid="8111151348106907513">"वायरलेस डिस्प्लेसम्बन्धी विकल्प देखाउनुहोस्"</string>
+ <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi-Fi लगिङ लेभल बढाउनुहोस्, Wi-Fi पिकरमा प्रति SSID RSSI देखाउनुहोस्"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"यसले ब्याट्रीको खपत कम गर्छ र नेटवर्कको कार्यसम्पादनमा सुधार गर्दछ"</string>
<string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"यो मोड अन गरिएका बेला यो डिभाइस म्याक एड्रेस बदल्ने सुविधा अन गरिएको नेटवर्कमा जति पटक कनेक्ट हुन्छ त्यति नै पटक यस डिभाइसको म्याक एड्रेस पनि परिवर्तन हुन सक्छ।"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"सशुल्क वाइफाइ"</string>
@@ -339,15 +328,15 @@
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"लग बफर प्रति लगर आकार चयन गर्नुहोस्"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"लगरको निरन्तर भण्डारणलाई खाली गर्ने हो?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"हामी अब निरन्तर लगर मार्फत अनुगमन गरिरहेका छैनौँ, त्यसैले हामीले तपाईँको डिभाइसमा रहेको लगर सम्बन्धी डेटा मेटाउन आवश्यक छ।"</string>
- <string name="select_logpersist_title" msgid="447071974007104196">"लगरसम्बन्धी डेटा निरन्तर डिभाइसमा भण्डारण गरियोस्"</string>
+ <string name="select_logpersist_title" msgid="447071974007104196">"लगरसम्बन्धी डेटा निरन्तर डिभाइसमा भण्डारण गर्नुहोस्"</string>
<string name="select_logpersist_dialog_title" msgid="7745193591195485594">"डिभाइसमा निरन्तर भण्डारण गरिने लग सम्बन्धी बफरहरूलाई चयन गर्नुहोस्"</string>
<string name="select_usb_configuration_title" msgid="6339801314922294586">"USB विन्यास चयन गर्नुहोस्"</string>
<string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"USB विन्यास चयन गर्नुहोस्"</string>
<string name="allow_mock_location" msgid="2102650981552527884">"नक्कली स्थानहरूलाई अनुमति दिनुहोस्"</string>
<string name="allow_mock_location_summary" msgid="179780881081354579">"नक्कली स्थानहरूलाई अनुमति दिनुहोस्"</string>
- <string name="debug_view_attributes" msgid="3539609843984208216">"भ्युको एट्रिब्युट हेर्ने सुविधा अन गरियोस्"</string>
+ <string name="debug_view_attributes" msgid="3539609843984208216">"भ्युको एट्रिब्युट हेर्ने सुविधा अन गर्नुहोस्"</string>
<string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Wi-Fi अन हुँदा पनि मोबाइल डेटा सधैँ अन होस् (द्रुत रूपमा नेटवर्क बदल्न)।"</string>
- <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"उपलब्ध हुँदा टेदरिङ हार्डवेयर एक्सलरेसन प्रयोग गरियोस्"</string>
+ <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"उपलब्ध हुँदा टेदरिङ हार्डवेयर एक्सलरेसन प्रयोग गर्नुहोस्"</string>
<string name="adb_warning_title" msgid="7708653449506485728">"USB डिबग गर्न लागि अनुमति दिने हो?"</string>
<string name="adb_warning_message" msgid="8145270656419669221">"युएसबी डिबगिङ विकास प्रयोजनका लागि मात्र निर्मित हुन्छ। यसलाई तपाईँको कम्प्युटर र तपाईँको उपकरणका बीच डेटा कपी गर्न, बिना सूचना तपाईँको उपकरणमा एपहरू इन्स्टल गर्न र लग डेटा पढ्नका लागि प्रयोग गर्नुहोस्।"</string>
<string name="adbwifi_warning_title" msgid="727104571653031865">"वायरलेस डिबगिङ सेवा सक्षम पार्ने हो?"</string>
@@ -355,11 +344,11 @@
<string name="adb_keys_warning_message" msgid="2968555274488101220">"तपाईं पहिले नै अधिकृत गर्नुभएका सबै कम्प्यूटरबाट USB डिबग गर्नको लागि पहुँच रद्द गर्ने हो?"</string>
<string name="dev_settings_warning_title" msgid="8251234890169074553">"विकास सेटिङहरू अनुमति दिने हो?"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"यी सेटिङहरू केवल विकास प्रयोगको लागि विचार गरिएको हो। तिनीहरूले तपाईंको उपकरण र एपहरूलाई विच्छेदन गर्न वा दुर्व्यवहार गर्न सक्दछ।"</string>
- <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB मा एपको पुष्टि गरियोस्"</string>
- <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"हानिकारक व्यवहार पत्ता लगाउन ADB/ADT बाट इन्स्टल गरिएका एपको जाँच गरियोस्"</string>
+ <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB मा एपको पुष्टि गर्नुहोस्"</string>
+ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"हानिकारक व्यवहार पत्ता लगाउन ADB/ADT बाट इन्स्टल गरिएका एपको जाँच गर्नुहोस्"</string>
<string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"नामकरण नगरिएका ब्लुटुथ डिभाइस (म्याक एड्रेस भएका मात्र) देखाइने छ"</string>
<string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"यसले रिमोट डिभाइसमा अत्यधिक ठूलो वा अनियन्त्रित भोल्युम बज्नेको जस्ता अवस्थामा ब्लुटुथको निरपेक्ष भोल्युम अफ गर्छ।"</string>
- <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ब्लुटुथ Gabeldorsche सुविधाको स्ट्याक अन गरियोस्।"</string>
+ <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ब्लुटुथ Gabeldorsche सुविधाको स्ट्याक अन गर्नुहोस्।"</string>
<string name="enhanced_connectivity_summary" msgid="1576414159820676330">"यसले परिष्कृत जडानको सुविधा सक्षम पार्छ।"</string>
<string name="enable_terminal_title" msgid="3834790541986303654">"स्थानीय टर्मिनल"</string>
<string name="enable_terminal_summary" msgid="2481074834856064500">"स्थानीय सेल पहुँच प्रदान गर्ने टर्मिनल एप सक्षम गर्नुहोस्"</string>
@@ -378,60 +367,60 @@
<string name="debug_hw_drawing_category" msgid="5830815169336975162">"हार्डवेयरले बढाएको रेन्डरिङ"</string>
<string name="media_category" msgid="8122076702526144053">"मिडिया"</string>
<string name="debug_monitoring_category" msgid="1597387133765424994">"अनुगमन गरिँदै छ"</string>
- <string name="strict_mode" msgid="889864762140862437">"स्ट्रिक्ट मोड अन गरियोस्"</string>
- <string name="strict_mode_summary" msgid="1838248687233554654">"एपले मुख्य थ्रेडमा लामा गतिविधि गर्दा स्क्रिन फ्ल्यास गरियोस्"</string>
+ <string name="strict_mode" msgid="889864762140862437">"स्ट्रिक्ट मोड अन गर्नुहोस्"</string>
+ <string name="strict_mode_summary" msgid="1838248687233554654">"एपले मुख्य थ्रेडमा लामा गतिविधि गर्दा स्क्रिन फ्ल्यास गर्नुहोस्"</string>
<string name="pointer_location" msgid="7516929526199520173">"पोइन्टरको स्थान"</string>
<string name="pointer_location_summary" msgid="957120116989798464">"स्क्रिन ओभरलेले हालको टच डेटा देखाउँदै छ"</string>
- <string name="show_touches" msgid="8437666942161289025">"ट्याप देखाइयोस्"</string>
- <string name="show_touches_summary" msgid="3692861665994502193">"ट्यापका लागि भिजुअल प्रतिक्रिया देखाइयोस्"</string>
+ <string name="show_touches" msgid="8437666942161289025">"ट्याप देखाउनुहोस्"</string>
+ <string name="show_touches_summary" msgid="3692861665994502193">"ट्यापका लागि भिजुअल प्रतिक्रिया देखाउनुहोस्"</string>
<string name="show_key_presses" msgid="6360141722735900214">"थिचिएका कीहरू देखाइयून्"</string>
- <string name="show_key_presses_summary" msgid="725387457373015024">"थिचिएका भौतिक कीसम्बन्धी भिजुअल प्रतिक्रिया देखाइयोस्"</string>
- <string name="show_screen_updates" msgid="2078782895825535494">"सर्फेस अपडेट देखाइयोस्"</string>
- <string name="show_screen_updates_summary" msgid="2126932969682087406">"अपडेट हुँदा विन्डोका पूरै सतहमा देखाइयोस्"</string>
- <string name="show_hw_screen_updates" msgid="2021286231267747506">"GPU भ्युको अपडेट देखाइयोस्"</string>
- <string name="show_hw_screen_updates_summary" msgid="3539770072741435691">"GPU ले बनाएको भ्यु विन्डोमा फ्ल्यास गरियोस्"</string>
- <string name="show_hw_layers_updates" msgid="5268370750002509767">"हार्डवेयर लेयरको अपडेट देखाइयोस्"</string>
+ <string name="show_key_presses_summary" msgid="725387457373015024">"थिचिएका भौतिक कीसम्बन्धी भिजुअल प्रतिक्रिया देखाउनुहोस्"</string>
+ <string name="show_screen_updates" msgid="2078782895825535494">"सर्फेस अपडेट देखाउनुहोस्"</string>
+ <string name="show_screen_updates_summary" msgid="2126932969682087406">"अपडेट हुँदा विन्डोका पूरै सतहमा देखाउनुहोस्"</string>
+ <string name="show_hw_screen_updates" msgid="2021286231267747506">"GPU भ्युको अपडेट देखाउनुहोस्"</string>
+ <string name="show_hw_screen_updates_summary" msgid="3539770072741435691">"GPU ले बनाएको भ्यु विन्डोमा फ्ल्यास गर्नुहोस्"</string>
+ <string name="show_hw_layers_updates" msgid="5268370750002509767">"हार्डवेयर लेयरको अपडेट देखाउनुहोस्"</string>
<string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"हार्डवेयर लेयर अपडेट हुँदा ती लेयर हरिया देखिऊन्"</string>
- <string name="debug_hw_overdraw" msgid="8944851091008756796">"GPU overdraw डिबग गरियोस्"</string>
- <string name="disable_overlays" msgid="4206590799671557143">"HW ओभरले अफ गरियोस्"</string>
- <string name="disable_overlays_summary" msgid="1954852414363338166">"स्क्रिन कोम्पजिट गर्न लागि सधैँ GPU प्रयोग गरियोस्"</string>
- <string name="simulate_color_space" msgid="1206503300335835151">"कलर स्पेसको नक्कल गरियोस्"</string>
+ <string name="debug_hw_overdraw" msgid="8944851091008756796">"GPU overdraw डिबग गर्नुहोस्"</string>
+ <string name="disable_overlays" msgid="4206590799671557143">"HW ओभरले अफ गर्नुहोस्"</string>
+ <string name="disable_overlays_summary" msgid="1954852414363338166">"स्क्रिन कोम्पजिट गर्न लागि सधैँ GPU प्रयोग गर्नुहोस्"</string>
+ <string name="simulate_color_space" msgid="1206503300335835151">"कलर स्पेसको नक्कल गर्नुहोस्"</string>
<string name="enable_opengl_traces_title" msgid="4638773318659125196">"OpenGL ट्रेसहरू सक्षम गर्नुहोस्"</string>
- <string name="usb_audio_disable_routing" msgid="3367656923544254975">"USB अडियो राउटिङ अफ गरियोस्"</string>
- <string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"USB अडियोमा स्वत: राउट नगरियोस्"</string>
- <string name="debug_layout" msgid="1659216803043339741">"लेआउटका सीमाहरू देखाइयोस्"</string>
- <string name="debug_layout_summary" msgid="8825829038287321978">"क्लिप सीमा, मार्जिन, इत्यादि देखाइयोस्।"</string>
- <string name="force_rtl_layout_all_locales" msgid="8690762598501599796">"RTL लेआउट बलपूर्वक प्रयोग गरियोस्"</string>
- <string name="force_rtl_layout_all_locales_summary" msgid="6663016859517239880">"सबै लोकेलमा RTLमा स्क्रिन लेआउट बलपूर्वक प्रयोग गरियोस्"</string>
+ <string name="usb_audio_disable_routing" msgid="3367656923544254975">"USB अडियो राउटिङ अफ गर्नुहोस्"</string>
+ <string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"USB अडियोमा स्वत: राउट नगर्नुहोस्"</string>
+ <string name="debug_layout" msgid="1659216803043339741">"लेआउटका सीमाहरू देखाउनुहोस्"</string>
+ <string name="debug_layout_summary" msgid="8825829038287321978">"क्लिप सीमा, मार्जिन, इत्यादि देखाउनुहोस्।"</string>
+ <string name="force_rtl_layout_all_locales" msgid="8690762598501599796">"RTL लेआउट बलपूर्वक प्रयोग गर्नुहोस्"</string>
+ <string name="force_rtl_layout_all_locales_summary" msgid="6663016859517239880">"सबै लोकेलमा RTLमा स्क्रिन लेआउट बलपूर्वक प्रयोग गर्नुहोस्"</string>
<string name="transparent_navigation_bar" msgid="1933192171384678484">"पारदर्शी नेभिगेसन बार"</string>
<string name="transparent_navigation_bar_summary" msgid="5454359021817330722">"नेभिगेसन बारको ब्याकग्राउन्डको रङ स्वतः पारदर्शी बनाउनुहोस्"</string>
- <string name="window_blurs" msgid="6831008984828425106">"विन्डो ब्लर गरियोस्"</string>
- <string name="force_msaa" msgid="4081288296137775550">"बलपूर्वक 4x MSAA प्रयोग गरियोस्"</string>
- <string name="force_msaa_summary" msgid="9070437493586769500">"OpenGL ES २.० एपमा ४x MSAA अन गरियोस्"</string>
- <string name="show_non_rect_clip" msgid="7499758654867881817">"गैर आयातकर क्लिप रहेका कार्यहरू डिबग गरियोस्"</string>
+ <string name="window_blurs" msgid="6831008984828425106">"विन्डो ब्लर गर्नुहोस्"</string>
+ <string name="force_msaa" msgid="4081288296137775550">"बलपूर्वक 4x MSAA प्रयोग गर्नुहोस्"</string>
+ <string name="force_msaa_summary" msgid="9070437493586769500">"OpenGL ES २.० एपमा ४x MSAA अन गर्नुहोस्"</string>
+ <string name="show_non_rect_clip" msgid="7499758654867881817">"गैर आयातकर क्लिप रहेका कार्यहरू डिबग गर्नुहोस्"</string>
<string name="track_frame_time" msgid="522674651937771106">"प्रोफाइलको HWUI रेन्डरिङ"</string>
- <string name="enable_gpu_debug_layers" msgid="4986675516188740397">"GPU का डिबग लेयर अन गरियोस्"</string>
- <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"डिबग एपका लागि GPU का डिबग लेयर लोड गरियोस्"</string>
- <string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"भर्बोज भेन्डर लगिङ अन गरियोस्"</string>
- <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"बग रिपोर्टहरूमा डिभाइस विशेषका विक्रेताका अतिरिक्त लगहरू समावेश गरियोस्। यी लगमा निजी जानकारी समावेश हुन सक्छन्, यिनले ब्याट्रीको खपत बढाउन र/वा थप भण्डारण प्रयोग गर्न सक्छन्।"</string>
+ <string name="enable_gpu_debug_layers" msgid="4986675516188740397">"GPU का डिबग लेयर अन गर्नुहोस्"</string>
+ <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"डिबग एपका लागि GPU का डिबग लेयर लोड गर्नुहोस्"</string>
+ <string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"भर्बोज भेन्डर लगिङ अन गर्नुहोस्"</string>
+ <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"बग रिपोर्टहरूमा डिभाइस विशेषका विक्रेताका अतिरिक्त लगहरू समावेश गर्नुहोस्। यी लगमा निजी जानकारी समावेश हुन सक्छन्, यिनले ब्याट्रीको खपत बढाउन र/वा थप भण्डारण प्रयोग गर्न सक्छन्।"</string>
<string name="window_animation_scale_title" msgid="5236381298376812508">"विन्डो एनिमेसन स्केल"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"संक्रमण एनिमेसन स्केल"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"एनिमेसनको अवधि मापन"</string>
- <string name="overlay_display_devices_title" msgid="5411894622334469607">"सहायक डिस्प्लेको नक्कल गरियोस्"</string>
+ <string name="overlay_display_devices_title" msgid="5411894622334469607">"सहायक डिस्प्लेको नक्कल गर्नुहोस्"</string>
<string name="debug_applications_category" msgid="5394089406638954196">"एपहरू"</string>
<string name="immediately_destroy_activities" msgid="1826287490705167403">"गतिविधि नराखियोस्"</string>
- <string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"प्रयोगकर्ता कुनै गतिविधिबाट बाहिरिने बित्तिकै उक्त गतिविधि अन्त्य गरियोस्"</string>
+ <string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"प्रयोगकर्ता कुनै गतिविधिबाट बाहिरिने बित्तिकै उक्त गतिविधि अन्त्य गर्नुहोस्"</string>
<string name="app_process_limit_title" msgid="8361367869453043007">"ब्याकग्राउन्ड प्रक्रियाको सीमा"</string>
- <string name="show_all_anrs" msgid="9160563836616468726">"ब्याकग्राउन्डमा ANR देखाइयोस्"</string>
- <string name="show_all_anrs_summary" msgid="8562788834431971392">"ब्याकग्राउन्डका एपको हकमा \'नचलिरहेका एप\' सन्देश देखाइयोस्"</string>
- <string name="show_notification_channel_warnings" msgid="3448282400127597331">"सूचना च्यानलसम्बन्धी चेतावनी देखाइयोस्"</string>
- <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"एपले मान्य च्यानलबिना सूचना पोस्ट गर्दा स्क्रिनमा चेतावनी देखाइयोस्"</string>
+ <string name="show_all_anrs" msgid="9160563836616468726">"ब्याकग्राउन्डमा ANR देखाउनुहोस्"</string>
+ <string name="show_all_anrs_summary" msgid="8562788834431971392">"ब्याकग्राउन्डका एपको हकमा \'नचलिरहेका एप\' सन्देश देखाउनुहोस्"</string>
+ <string name="show_notification_channel_warnings" msgid="3448282400127597331">"सूचना च्यानलसम्बन्धी चेतावनी देखाउनुहोस्"</string>
+ <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"एपले मान्य च्यानलबिना सूचना पोस्ट गर्दा स्क्रिनमा चेतावनी देखाउनुहोस्"</string>
<string name="force_allow_on_external" msgid="9187902444231637880">"एपलाई बहिरी मेमोरीमा पनि चल्न दिइयोस्"</string>
- <string name="force_allow_on_external_summary" msgid="8525425782530728238">"तोकिएको नियमको ख्याल नगरी एपलाई बाह्य भण्डारणमा चल्ने बनाइयोस्"</string>
- <string name="force_resizable_activities" msgid="7143612144399959606">"बलपूर्वक एपहरूको आकार मिलाउन मिल्ने बनाइयोस्"</string>
- <string name="force_resizable_activities_summary" msgid="2490382056981583062">"तोकिएको नियमको ख्याल नगरी एपलाई एकभन्दा बढी विन्डोमा रिसाइज गर्न सकिने बनाइयोस्।"</string>
- <string name="enable_freeform_support" msgid="7599125687603914253">"फ्रिफर्म विन्डोहरू अन गरियोस्"</string>
- <string name="enable_freeform_support_summary" msgid="1822862728719276331">"प्रयोगात्मक फ्रिफर्म विन्डोहरू चल्ने बनाइयोस्"</string>
+ <string name="force_allow_on_external_summary" msgid="8525425782530728238">"तोकिएको नियमको ख्याल नगरी एपलाई बाह्य भण्डारणमा चल्ने बनाउनुहोस्"</string>
+ <string name="force_resizable_activities" msgid="7143612144399959606">"बलपूर्वक एपहरूको आकार मिलाउन मिल्ने बनाउनुहोस्"</string>
+ <string name="force_resizable_activities_summary" msgid="2490382056981583062">"तोकिएको नियमको ख्याल नगरी एपलाई एकभन्दा बढी विन्डोमा रिसाइज गर्न सकिने बनाउनुहोस्।"</string>
+ <string name="enable_freeform_support" msgid="7599125687603914253">"फ्रिफर्म विन्डोहरू अन गर्नुहोस्"</string>
+ <string name="enable_freeform_support_summary" msgid="1822862728719276331">"प्रयोगात्मक फ्रिफर्म विन्डोहरू चल्ने बनाउनुहोस्"</string>
<string name="local_backup_password_title" msgid="4631017948933578709">"डेस्कटप ब्याकअप पासवर्ड"</string>
<string name="local_backup_password_summary_none" msgid="7646898032616361714">"हाल डेस्कटपका सबै ब्याकअप पासवर्ड सुरक्षित छैनन्"</string>
<string name="local_backup_password_summary_change" msgid="1707357670383995567">"डेस्कटप पूर्ण ब्याकअपको लागि पासवर्ड बदल्न वा हटाउन ट्याप गर्नुहोस्"</string>
@@ -457,7 +446,7 @@
<string name="transcode_user_control" msgid="6176368544817731314">"ट्रान्सकोडिङसम्बन्धी डिफल्ट सेटिङ परिवर्तन गर्नुहोस्"</string>
<string name="transcode_enable_all" msgid="2411165920039166710">"ट्रान्सकोडिङ अन गर्नुहोस्"</string>
<string name="transcode_default" msgid="3784803084573509491">"एपहरूमा आधुनिक फर्म्याट प्रयोग गर्न मिल्छ भनी मान्नुहोस्"</string>
- <string name="transcode_notification" msgid="5560515979793436168">"ट्रान्सकोडिङसम्बन्धी सूचना देखाइयोस्"</string>
+ <string name="transcode_notification" msgid="5560515979793436168">"ट्रान्सकोडिङसम्बन्धी सूचना देखाउनुहोस्"</string>
<string name="transcode_disable_cache" msgid="3160069309377467045">"ट्रान्सकोडिङको क्यास अफ गर्नुहोस्"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"चलिरहेका सेवाहरू"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"हाल चालु भइरहेका सेवाहरू हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string>
@@ -472,7 +461,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"प्रोटानेमली (रातो, हरियो)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ट्रिटानोमेली (निलो-पंहेलो)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"कलर करेक्सन"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"तपाईं रङ सच्याउने सुविधाका सहायताले निम्न कार्य गर्न सक्नुहुन्छ:<br/> <ol> <li>&nbsp;अझ सटीक तरिकाले रङहरू हेर्न</li> <li>&nbsp;फोकस गर्नका लागि रङहरू हटाउन</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"तपाईं कलर करेक्सनका सहायताले निम्न कार्य गर्न सक्नुहुन्छ:<br/> <ol> <li>&nbsp;अझ सटीक तरिकाले रङहरू हेर्न</li> <li>&nbsp;फोकस गर्नका लागि रङहरू हटाउन</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ब्याट्री जोगाउन चार्जिङ होल्ड गरिएको छ"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूरा चार्ज हुन <xliff:g id="TIME">%2$s</xliff:g> लाग्ने छ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज गर्ने प्रक्रिया अप्टिमाइज गरिएको छ"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज गरिँदै छ"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै छ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै छ"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"चार्ज भयो"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"पूर्ण रूपमा चार्ज भएको छ"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"चार्जिङ होल्ड गरिएको छ"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"प्रशासकद्वारा नियन्त्रित"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"प्रतिबन्धित सेटिङले नियन्त्रण गरेको"</string>
<string name="disabled" msgid="8017887509554714950">"असक्षम पारियो"</string>
@@ -534,7 +535,7 @@
<string name="retail_demo_reset_next" msgid="3688129033843885362">"अर्को"</string>
<string name="retail_demo_reset_title" msgid="1866911701095959800">"पासवर्ड आवश्यक छ"</string>
<string name="active_input_method_subtypes" msgid="4232680535471633046">"आगत विधिहरू अन गर्नुहोस्"</string>
- <string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"सिष्टममा भएका भाषा प्रयोग गरियोस्"</string>
+ <string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"सिष्टममा भएका भाषा प्रयोग गर्नुहोस्"</string>
<string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>का लागि सेटिङहरू खोल्न विफल भयो।"</string>
<string name="ime_security_warning" msgid="6547562217880551450">"यस इनपुट विधिले तपाईँले टाइप गर्नुहुने सम्पूर्ण पाठ बटु्ल्न सक्छ, व्यक्तिगत डेटा जस्तै पासवर्ड र क्रेडिट कार्ड नम्बर लगायतका। यो <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> अनुप्रयोगबाट आउँदछ। यो इनपुट विधि प्रयोग गर्ने हो?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"नोट: रिबुट गरेपछि तपाईंले आफ्नो फोन अनलक नगरेसम्म यो एप सुरु हुँदैन"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"फोन एउटा पट्टि।"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"फोन दुई पट्टि।"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"फोन तिन पट्टिहरू।"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"फोन सङ्केत भरिएको।"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"डेटा छैन।"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"डेटाको एउटा पट्टि।"</string>
@@ -708,8 +711,8 @@
<string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"किबोर्ड लेआउट छान्नुहोस्"</string>
<string name="keyboard_layout_default_label" msgid="1997292217218546957">"डिफल्ट"</string>
<string name="turn_screen_on_title" msgid="3266937298097573424">"स्क्रिन अन गर्नुहोस्"</string>
- <string name="allow_turn_screen_on" msgid="6194845766392742639">"स्क्रिन अन गर्ने अनुमति दिइयोस्"</string>
- <string name="allow_turn_screen_on_description" msgid="43834403291575164">"कुनै एपलाई स्क्रिन अन गर्ने अनुमति दिइयोस्। यो अनुमति दिइएका खण्डमा तपाईंले अन गर्न नखोजेका बेलामा पनि एपले जुनसुकै बेला स्क्रिन अन गर्न सक्छ।"</string>
+ <string name="allow_turn_screen_on" msgid="6194845766392742639">"स्क्रिन अन गर्ने अनुमति दिनुहोस्"</string>
+ <string name="allow_turn_screen_on_description" msgid="43834403291575164">"कुनै एपलाई स्क्रिन अन गर्ने अनुमति दिनुहोस्। यो अनुमति दिइएका खण्डमा तपाईंले अन गर्न नखोजेका बेलामा पनि एपले जुनसुकै बेला स्क्रिन अन गर्न सक्छ।"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g> ब्रोडकास्ट गर्न छाड्ने हो?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"तपाईंले <xliff:g id="SWITCHAPP">%1$s</xliff:g> ब्रोडकास्ट गर्नुभयो वा आउटपुट परिवर्तन गर्नुभयो भने तपाईंको हालको ब्रोडकास्ट रोकिने छ"</string>
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ब्रोडकास्ट गर्नुहोस्"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 801c73f..fb7e4c2 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -106,35 +106,24 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Actief, alleen links"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Actief, alleen rechts"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Actief, links en rechts"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Actief (alleen media), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batterij"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Actief (alleen media), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batterij, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batterij"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Verbonden (ondersteunt audio delen), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batterij"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Verbonden (ondersteunt audio delen), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batterij, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batterij"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Verbonden (ondersteunt audio delen), links <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Verbonden (ondersteunt audio delen), rechts <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Actief (alleen media)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Ondersteunt audio delen"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Actief (alleen media), alleen links"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Actief (alleen media), alleen rechts"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Actief (alleen media), links en rechts"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Media-audio"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefoongesprekken"</string>
<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>
<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_pbap_summary" msgid="402819589201138227">"Deze informatie wordt o.a. 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">"Simtoegang"</string>
@@ -472,7 +461,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (rood-groen)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (blauw-geel)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Kleurcorrectie"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Kleurcorrectie kan handig zijn in de volgende situaties:<br/> <ol> <li>&nbsp;Je wilt kleuren nauwkeuriger zien.</li> <li>&nbsp;Je wilt kleuren verwijderen zodat je je beter kunt focussen.</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Kleurcorrectie kan handig zijn in de volgende situaties:<br/> <ol> <li>&nbsp;Je wilt kleuren nauwkeuriger zien.</li> <li>&nbsp;Je wilt kleuren verwijderen zodat je je beter kunt concentreren.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overschreven door <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>: opladen is in de wacht gezet om de batterij te beschermen"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - vol over <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Opladen geoptimaliseerd"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Opladen"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Opladen"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Snel opladen"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Opgeladen"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Volledig opgeladen"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Opladen in de wacht"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Ingesteld door beheerder"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Beheerd door beperkte instelling"</string>
<string name="disabled" msgid="8017887509554714950">"Uitgezet"</string>
@@ -553,7 +554,7 @@
<string name="okay" msgid="949938843324579502">"OK"</string>
<string name="done" msgid="381184316122520313">"Klaar"</string>
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Wekkers en herinneringen"</string>
- <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Wekkers en herinneringen laten instellen"</string>
+ <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Instellen van wekkers en herinneringen toestaan"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wekkers en herinneringen"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Sta toe dat deze app wekkers zet en tijdgevoelige acties plant. De app kan hierdoor op de achtergrond worden uitgevoerd, waardoor je misschien meer batterijlading verbruikt.\n\nAls dit recht uitstaat, werken door deze app geplande bestaande wekkers en tijdgebaseerde afspraken niet."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"plannen, schema, wekker, alarm, herinnering, klok"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefoon: één streepje."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefoon: twee streepjes."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefoon: drie streepjes."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefoonsignaal is op volle sterkte."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Geen gegevens."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Gegevens: één streepje."</string>
@@ -709,7 +712,7 @@
<string name="keyboard_layout_default_label" msgid="1997292217218546957">"Standaard"</string>
<string name="turn_screen_on_title" msgid="3266937298097573424">"Scherm aanzetten"</string>
<string name="allow_turn_screen_on" msgid="6194845766392742639">"Scherm aanzetten toestaan"</string>
- <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Toestaan dat een app het scherm aanzet. Indien toegestaan, kan de app het scherm op elk moment aanzetten zonder jouw expliciete intentie."</string>
+ <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Sta toe dat een app het scherm aanzet. Indien toegestaan, kan de app het scherm op elk moment aanzetten zonder jouw expliciete intentie."</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Uitzending van <xliff:g id="APP_NAME">%1$s</xliff:g> stopzetten?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Als je <xliff:g id="SWITCHAPP">%1$s</xliff:g> uitzendt of de uitvoer wijzigt, wordt je huidige uitzending gestopt"</string>
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> uitzenden"</string>
diff --git a/packages/SettingsLib/res/values-or/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml
index c7c857b..28a8db6 100644
--- a/packages/SettingsLib/res/values-or/arrays.xml
+++ b/packages/SettingsLib/res/values-or/arrays.xml
@@ -188,7 +188,7 @@
<item msgid="409235464399258501">"ବନ୍ଦ"</item>
<item msgid="4195153527464162486">"64K ପିଛା ଲଗ୍ ବଫର୍"</item>
<item msgid="7464037639415220106">"256K ଲଗ୍ ପ୍ରତି ବଫର୍"</item>
- <item msgid="8539423820514360724">"1M ପ୍ରତି ଲଗ୍ ବଫର୍"</item>
+ <item msgid="8539423820514360724">"ପ୍ରତି ଲଗ ବଫର ପାଇଁ 1M"</item>
<item msgid="1984761927103140651">"ଲଗ୍ ବଫର୍ ପ୍ରତି 4M"</item>
<item msgid="2983219471251787208">"ଲଗ୍ ବଫର୍ ପ୍ରତି 8M"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 3cbbc05..180d5cd 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -96,7 +96,7 @@
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"ସଂଯୁକ୍ତ ହେଲା (ଫୋନ୍ କିମ୍ବା ମେଡିଆ ନୁହେଁ), ବ୍ୟାଟେରୀ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"ସକ୍ରିୟ, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ବ୍ୟାଟେରୀ"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"ସକ୍ରିୟ, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ବ୍ୟାଟେରୀ, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ବ୍ୟାଟେରୀ"</string>
- <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ବ୍ୟାଟେରୀ"</string>
+ <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ବେଟେରୀ"</string>
<string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"ବେଟେରୀ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ବ୍ୟାଟେରୀ, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ବ୍ୟାଟେରୀ"</string>
<string name="bluetooth_battery_level_untethered_left" msgid="2952823007648782646">"ବାମ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"ସକ୍ରିୟ, କେବଳ ବାମ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"ସକ୍ରିୟ, କେବଳ ଡାହାଣ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"ସକ୍ରିୟ, ବାମ ଏବଂ ଡାହାଣ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"ସକ୍ରିୟ (କେବଳ ମିଡିଆ) <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ବେଟେରୀ"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"ସକ୍ରିୟ (କେବଳ ମିଡିଆ), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ବେଟେରୀ, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ବେଟେରୀ"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"କନେକ୍ଟ ହୋଇଛି (ଅଡିଓ ସେୟାରିଂକୁ ସମର୍ଥନ କରେ), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ବେଟେରୀ"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"କନେକ୍ଟ ହୋଇଛି (ଅଡିଓ ସେୟାରିଂକୁ ସମର୍ଥନ କରେ), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ବେଟେରୀ, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ବେଟେରୀ"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"କନେକ୍ଟ ହୋଇଛି (ଅଡିଓ ସେୟାରିଂକୁ ସମର୍ଥନ କରେ), ବାମ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"କନେକ୍ଟ ହୋଇଛି (ଅଡିଓ ସେୟାରିଂକୁ ସମର୍ଥନ କରେ), ଡାହାଣ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"ସକ୍ରିୟ (କେବଳ ମିଡିଆ)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ଅଡିଓ ସେୟାରିଂକୁ ସମର୍ଥନ କରେ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"ସକ୍ରିୟ (କେବଳ ମିଡିଆ), କେବଳ ବାମ"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"ସକ୍ରିୟ (କେବଳ ମିଡିଆ), କେବଳ ଡାହାଣ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"ସକ୍ରିୟ (କେବଳ ମିଡିଆ), ବାମ ଏବଂ ଡାହାଣ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"ମିଡିଆ ଅଡିଓ"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ଫୋନ୍ କଲ୍ଗୁଡ଼ିକ"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ଫାଇଲ୍ ଟ୍ରାନ୍ସଫର୍"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - ପୂର୍ଣ୍ଣ ହେବାକୁ ଆଉ <xliff:g id="TIME">%2$s</xliff:g> ବାକି ଅଛି"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ଚାର୍ଜିଂକୁ ଅପ୍ଟିମାଇଜ କରାଯାଇଛି"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ ଚାର୍ଜିଂ"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ଅଜ୍ଞାତ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ଚାର୍ଜ ହେଉଛି"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"ଚାର୍ଜ ହୋଇଯାଇଛି"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"ସମ୍ପୂର୍ଣ୍ଣ ଭାବରେ ଚାର୍ଜ ହୋଇଛି"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ଚାର୍ଜିଂ ହୋଲ୍ଡରେ ଅଛି"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ଆଡ୍ମିନ୍ ଦ୍ୱାରା ନିୟନ୍ତ୍ରିତ"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ପ୍ରତିବନ୍ଧିତ ସେଟିଂ ଦ୍ୱାରା ନିୟନ୍ତ୍ରଣ କରାଯାଇଛି"</string>
<string name="disabled" msgid="8017887509554714950">"ଅକ୍ଷମ ହୋଇଛି"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"ଫୋନର ଗୋଟିଏ ବାର ଅଛି।"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"ଫୋନର ଦୁଇଟି ବାର୍ ଅଛି।"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"ଫୋନ୍ରେ ତିନୋଟି ବାର୍ ଅଛି।"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"ଫୋନ୍ ସିଗ୍ନାଲ୍ ପୂର୍ଣ୍ଣ ଅଛି।"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"କୌଣସି ଡାଟା ନାହିଁ।"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"ଡାଟାର ଗୋଟିଏ ବାର ଅଛି।"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 2105852..d9be9bb 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"ਕਿਰਿਆਸ਼ੀਲ, ਸਿਰਫ਼ ਖੱਬਾ"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"ਕਿਰਿਆਸ਼ੀਲ, ਸਿਰਫ਼ ਸੱਜਾ"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"ਕਿਰਿਆਸ਼ੀਲ, ਖੱਬਾ ਅਤੇ ਸੱਜਾ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"ਕਿਰਿਆਸ਼ੀਲ (ਸਿਰਫ਼ ਮੀਡੀਆ), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ਬੈਟਰੀ"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"ਕਿਰਿਆਸ਼ੀਲ (ਸਿਰਫ਼ ਮੀਡੀਆ), ਖੱਬਾ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ਬੈਟਰੀ, ਸੱਜਾ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ਬੈਟਰੀ"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"ਕਨੈਕਟ ਕੀਤਾ (ਆਡੀਓ ਸਾਂਝਾਕਰਨ ਦਾ ਸਮਰਥਨ ਕਰਦਾ ਹੈ), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ਬੈਟਰੀ"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"ਕਨੈਕਟ ਕੀਤਾ (ਆਡੀਓ ਸਾਂਝਾਕਰਨ ਦਾ ਸਮਰਥਨ ਕਰਦਾ ਹੈ), ਖੱਬਾ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ਬੈਟਰੀ, ਸੱਜਾ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ਬੈਟਰੀ"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"ਕਨੈਕਟ ਕੀਤਾ (ਆਡੀਓ ਸਾਂਝਾਕਰਨ ਦਾ ਸਮਰਥਨ ਕਰਦਾ ਹੈ), ਖੱਬਾ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"ਕਨੈਕਟ ਕੀਤਾ (ਆਡੀਓ ਸਾਂਝਾਕਰਨ ਦਾ ਸਮਰਥਨ ਕਰਦਾ ਹੈ), ਸੱਜਾ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"ਕਿਰਿਆਸ਼ੀਲ (ਸਿਰਫ਼ ਮੀਡੀਆ)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ਆਡੀਓ ਸਾਂਝਾਕਰਨ ਦਾ ਸਮਰਥਨ ਕਰਦਾ ਹੈ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"ਕਿਰਿਆਸ਼ੀਲ (ਸਿਰਫ਼ ਮੀਡੀਆ), ਸਿਰਫ਼ ਖੱਬਾ"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"ਕਿਰਿਆਸ਼ੀਲ (ਸਿਰਫ਼ ਮੀਡੀਆ), ਸਿਰਫ਼ ਸੱਜਾ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"ਕਿਰਿਆਸ਼ੀਲ (ਸਿਰਫ਼ ਮੀਡੀਆ), ਖੱਬਾ ਅਤੇ ਸੱਜਾ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"ਮੀਡੀਆ ਆਡੀਓ"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ਫ਼ੋਨ ਕਾਲਾਂ"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਪੂਰੀ ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%2$s</xliff:g> ਬਾਕੀ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜਿੰਗ ਨੂੰ ਸੁਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ਅਗਿਆਤ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
@@ -506,9 +503,13 @@
<string name="battery_info_status_charging_dock" msgid="8573274094093364791">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
<string name="battery_info_status_not_charging" msgid="1103084691314264664">"ਕਨੈਕਟ ਹੋ ਗਿਆ, ਪਰ ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ ਹੈ"</string>
- <string name="battery_info_status_full" msgid="1339002294876531312">"ਚਾਰਜ ਹੋ ਗਈ"</string>
+ <string name="battery_info_status_full" msgid="1339002294876531312">"ਬੈਟਰੀ ਚਾਰਜ ਹੋ ਗਈ"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"ਪੂਰੀ ਚਾਰਜ ਹੋ ਗਈ ਹੈ"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ਚਾਰਜਿੰਗ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਕੰਟਰੋਲ ਕੀਤੀ ਗਈ"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ਪ੍ਰਤਿਬੰਧਿਤ ਸੈਟਿੰਗ ਰਾਹੀਂ ਕੰਟਰੋਲ ਕੀਤੀ ਜਾਂਦੀ ਹੈ"</string>
<string name="disabled" msgid="8017887509554714950">"ਅਯੋਗ ਬਣਾਇਆ"</string>
@@ -555,7 +556,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ ਸੈੱਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ਇਸ ਐਪ ਨੂੰ ਅਲਾਰਮ ਸੈੱਟ ਕਰਨ ਜਾਂ ਹੋਰ ਸਮਾਂ-ਸੰਵੇਦਨਸ਼ੀਲ ਕਾਰਵਾਈਆਂ ਨੂੰ ਨਿਯਤ ਕਰਨ ਦਿਓ। ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲਣ ਦੀ ਇਜਾਜ਼ਤ ਮਿਲਦੀ ਹੈ, ਜਿਸ ਨਾਲ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਵੱਧ ਸਕਦੀ ਹੈ।\n\nਜੇ ਇਹ ਇਜਾਜ਼ਤ ਬੰਦ ਹੈ, ਤਾਂ ਮੌਜੂਦਾ ਅਲਾਰਮ ਅਤੇ ਇਸ ਐਪ ਰਾਹੀਂ ਨਿਯਤ ਕੀਤੇ ਸਮਾਂ-ਆਧਾਰਿਤ ਇਵੈਂਟ ਕੰਮ ਨਹੀਂ ਕਰਨਗੇ।"</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ਇਸ ਐਪ ਨੂੰ ਅਲਾਰਮ ਸੈੱਟ ਕਰਨ ਜਾਂ ਹੋਰ ਸਮਾਂ-ਸੰਵੇਦਨਸ਼ੀਲ ਕਾਰਵਾਈਆਂ ਨੂੰ ਨਿਯਤ ਕਰਨ ਦਿਓ। ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲਣ ਦੀ ਇਜਾਜ਼ਤ ਮਿਲਦੀ ਹੈ, ਜਿਸ ਨਾਲ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਵੱਧ ਸਕਦੀ ਹੈ।\n\nਜੇ ਇਹ ਇਜਾਜ਼ਤ ਬੰਦ ਹੈ, ਤਾਂ ਇਸ ਐਪ ਰਾਹੀਂ ਨਿਯਤ ਕੀਤੇ ਮੌਜੂਦਾ ਅਲਾਰਮ ਅਤੇ ਸਮਾਂ-ਆਧਾਰਿਤ ਇਵੈਂਟ ਕੰਮ ਨਹੀਂ ਕਰਨਗੇ।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ਸਮਾਂ-ਸੂਚੀ, ਅਲਾਰਮ, ਰਿਮਾਈਂਡਰ, ਘੜੀ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ਚਾਲੂ ਕਰੋ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"ਫ਼ੋਨ ਇੱਕ ਬਾਰ।"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"ਫ਼ੋਨ ਦੋ ਬਾਰਸ।"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"ਫ਼ੋਨ ਤਿੰਨ ਬਾਰਸ।"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"ਫ਼ੋਨ ਸਿਗਨਲ ਪੂਰਾ।"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"ਕੋਈ ਡਾਟਾ ਨਹੀਂ।"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">" ਡਾਟਾ ਇੱਕ ਬਾਰ।"</string>
@@ -709,7 +712,7 @@
<string name="keyboard_layout_default_label" msgid="1997292217218546957">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string>
<string name="turn_screen_on_title" msgid="3266937298097573424">"ਸਕ੍ਰੀਨ ਚਾਲੂ ਕਰੋ"</string>
<string name="allow_turn_screen_on" msgid="6194845766392742639">"ਸਕ੍ਰੀਨ ਚਾਲੂ ਕਰਨ ਦਿਓ"</string>
- <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ਐਪ ਨੂੰ ਸਕ੍ਰੀਨ ਚਾਲੂ ਕਰਨ ਦਿਓ। ਜੇ ਇਜਾਜ਼ਤ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ, ਤਾਂ ਐਪ ਕਿਸੇ ਵੀ ਸਮੇਂ ਸਕ੍ਰੀਨ ਨੂੰ ਚਾਲੂ ਕਰ ਸਕਦੀ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਨੂੰ ਇਸਦੀ ਲੋੜ ਨਾ ਹੋਵੇ।"</string>
+ <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ਐਪ ਨੂੰ ਸਕ੍ਰੀਨ ਚਾਲੂ ਕਰਨ ਦਿਓ। ਇਜਾਜ਼ਤ ਦਿੱਤੇ ਜਾਣ \'ਤੇ, ਇਹ ਐਪ ਤੁਹਾਡੇ ਇਰਾਦੇ ਦੇ ਬਿਨਾਂ ਕਿਸੇ ਵੇਲੇ ਵੀ ਸਕ੍ਰੀਨ ਨੂੰ ਚਾਲੂ ਕਰ ਸਕਦੀ ਹੈ।"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੇ ਪ੍ਰਸਾਰਨ ਨੂੰ ਰੋਕਣਾ ਹੈ?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"ਜੇ ਤੁਸੀਂ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ਦਾ ਪ੍ਰਸਾਰਨ ਕਰਦੇ ਹੋ ਜਾਂ ਆਊਟਪੁੱਟ ਬਦਲਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਪ੍ਰਸਾਰਨ ਰੁਕ ਜਾਵੇਗਾ"</string>
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ਦਾ ਪ੍ਰਸਾਰਨ ਕਰੋ"</string>
diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml
index 5358ed8..9c25aaa 100644
--- a/packages/SettingsLib/res/values-pl/arrays.xml
+++ b/packages/SettingsLib/res/values-pl/arrays.xml
@@ -186,10 +186,10 @@
</string-array>
<string-array name="select_logd_size_summaries">
<item msgid="409235464399258501">"Wył."</item>
- <item msgid="4195153527464162486">"64 KB/bufor dziennika"</item>
- <item msgid="7464037639415220106">"256 KB/bufor dziennika"</item>
- <item msgid="8539423820514360724">"1 MB/bufor dziennika"</item>
- <item msgid="1984761927103140651">"4 MB/bufor dziennika"</item>
+ <item msgid="4195153527464162486">"64 KB / bufor dziennika"</item>
+ <item msgid="7464037639415220106">"256 KB / bufor dziennika"</item>
+ <item msgid="8539423820514360724">"1 MB / bufor dziennika"</item>
+ <item msgid="1984761927103140651">"4 MB / bufor dziennika"</item>
<item msgid="2983219471251787208">"8 MB na bufor dziennika"</item>
</string-array>
<string-array name="select_logpersist_titles">
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 93e65af..2eccb04 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -106,35 +106,24 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktywne, tylko lewa strona"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktywne, tylko prawa strona"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktywny, lewa i prawa strona"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktywne (tylko multimedia), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> naładowania baterii"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktywne (tylko multimedia), lewa: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> naładowania baterii, prawa: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> naładowania baterii"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Połączone (obsługa udostępniania dźwięku), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> naładowania baterii"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Połączone (obsługa udostępniania dźwięku), lewa: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> naładowania baterii, prawa: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> naładowania baterii"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Połączone (obsługa udostępniania dźwięku), lewa <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Połączone (obsługa udostępniania dźwięku), prawa <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktywne (tylko multimedia)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Obsługa udostępniania dźwięku"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktywne (tylko multimedia), tylko lewa"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktywne (tylko multimedia), tylko prawa"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktywne (tylko multimedia), lewa i prawa"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Dźwięk multimediów"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Połączenia telefoniczne"</string>
<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>
<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_pbap_summary" msgid="402819589201138227">"Informacje zostaną wykorzystane m.in. do powiadomień o połączeniach"</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>
@@ -336,7 +325,7 @@
<string name="wifi_metered_label" msgid="8737187690304098638">"Użycie danych jest mierzone"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Użycie danych nie jest mierzone"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Rozmiary bufora rejestratora"</string>
- <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Wybierz rozmiary Rejestratora/bufor dziennika"</string>
+ <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Wybierz rozmiary Rejestratora na bufor dziennika"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"Wyczyścić pamięć trwałych dzienników?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"Po zakończeniu monitorowania przy użyciu trwale zapisywanych dzienników musimy usunąć ich dane zapisane na urządzeniu."</string>
<string name="select_logpersist_title" msgid="447071974007104196">"Zapisuj trwale dane dzienników na urządzeniu"</string>
@@ -498,6 +487,10 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – ładowanie zoptymalizowane"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – ładowanie"</string>
+ <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Bateria będzie pełna do <xliff:g id="TIME">%3$s</xliff:g>"</string>
+ <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Pełne naładowanie do <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Pełne naładowanie do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Bateria będzie pełna do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string>
@@ -505,10 +498,12 @@
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Ładowanie bezprzewodowe"</string>
<string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Ładowanie"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nie podłączony"</string>
- <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Podłączono, ale nie ładuje się"</string>
+ <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Podłączono, brak ładowania"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Naładowana"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Bateria w pełni naładowana"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Ładowanie wstrzymane"</string>
+ <string name="battery_info_status_charging_v2" msgid="6118522107222245505">"Ładowanie"</string>
+ <string name="battery_info_status_charging_fast_v2" msgid="1825439848151256589">"Szybkie ładowanie"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolowane przez administratora"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Obowiązują ustawienia z ograniczonym dostępem"</string>
<string name="disabled" msgid="8017887509554714950">"Wyłączone"</string>
@@ -695,6 +690,7 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefon: jeden pasek."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefon: dwa paski."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefon: trzy paski."</string>
+ <string name="accessibility_phone_four_bars" msgid="4477202400261338403">"Cztery słupki w telefonie."</string>
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefon: pełna moc sygnału."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Brak danych."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Dane: jeden pasek."</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 811c04a..f37764a 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Ativo, apenas o esquerdo"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Ativo, apenas o direito"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Ativo, esquerdo e direito"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Ativo (apenas mídia), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Ativo (apenas mídia), E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Conectado (com suporte ao compartilhamento de áudio), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Conectado (com suporte ao compartilhamento de áudio), E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Conectado (com suporte ao compartilhamento de áudio), esquerdo <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Conectado (com suporte ao compartilhamento de áudio), direito <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Ativo (apenas mídia)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Com suporte ao compartilhamento de áudio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Ativo (apenas mídia), apenas esquerdo"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Ativo (apenas mídia), somente direito"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Ativo (apenas mídia), esquerdo e direito"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Áudio da mídia"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Chamadas telefônicas"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência de arquivo"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a conclusão"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (carregando)"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Carga completa"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Carregamento suspenso"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada pelo admin"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlada pelas configurações restritas"</string>
<string name="disabled" msgid="8017887509554714950">"Desativado"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Uma barra de sinal do telefone."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Duas barras de sinal do telefone."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Três barras de sinal do telefone."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Sinal do telefone cheio."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Nenhum dado."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Uma barra de sinal de dados."</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 4c88683..850b6f3 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -106,35 +106,24 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Ativo, apenas esquerdo"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Ativo, apenas direito"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Ativo, esquerdo e direito"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Ativo (apenas para multimédia), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Ativo (apenas para multimédia), E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Ligado (suporta partilha de áudio), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Ligado (suporta partilha de áudio), E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Ligado (suporta partilha de áudio), esquerdo <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Ligado (suporta partilha de áudio), direito <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Ativo (apenas para multimédia)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Suporta partilha de áudio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Ativo (apenas para multimédia), apenas esquerdo"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Ativo (apenas para multimédia), apenas direito"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Ativo (apenas para multimédia), esquerdo e direito"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Áudio de multimédia"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Chamadas telefónicas"</string>
<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>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Acesso a contactos e histórico de chamadas"</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_pbap_summary" msgid="402819589201138227">"As informações são usadas para anúncios por 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>
@@ -410,7 +399,7 @@
<string name="force_msaa_summary" msgid="9070437493586769500">"Ativar o 4x MSAA em aplicações OpenGL ES 2.0"</string>
<string name="show_non_rect_clip" msgid="7499758654867881817">"Depurar operações de clipe não retangulares"</string>
<string name="track_frame_time" msgid="522674651937771106">"Renderização HWUI do perfil"</string>
- <string name="enable_gpu_debug_layers" msgid="4986675516188740397">"Ativar cam. depuração GPU"</string>
+ <string name="enable_gpu_debug_layers" msgid="4986675516188740397">"Ativar camadas de depuração GPU"</string>
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permite carregamento de camadas de depuração de GPU para apps de depuração"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Ativ. registo do fornecedor"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Inclua registos adicionais de fornecedores específicos de dispositivos em relatórios de erros, que podem conter informações privadas, utilizar mais bateria e/ou utilizar mais armazenamento."</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até à carga máxima"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g>: carregamento otimizado"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – A carregar"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"A carregar"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregamento rápido"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Totalmente carregada"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Carregamento em espera"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlado pelo gestor"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlado por uma definição restrita"</string>
<string name="disabled" msgid="8017887509554714950">"Desativada"</string>
@@ -555,7 +556,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmes e lembretes"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Permitir alarmes e lembretes"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes e lembretes"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permita que esta app defina alarmes e agende outras ações com base no tempo. Esta ação permite que a app seja executada em segundo plano, o que pode utilizar mais bateria.\n\nSe esta autorização estiver desativada, os alarmes existentes e os eventos com base no tempo agendados por esta app não funcionam."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permita que a app defina alarmes e agende ações com um horário específico. Esta ação permite que a app seja executada em segundo plano, o que pode usar mais bateria.\n\nSe esta autorização estiver desativada, os alarmes existentes e os eventos com base no tempo agendados por esta app não funcionam."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"agendar, alarme, lembrete, relógio"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ativar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ativar o modo Não incomodar"</string>
@@ -595,7 +596,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Altifalantes internos"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema ao ligar. Desligue e volte a ligar o dispositivo."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fios"</string>
- <string name="help_label" msgid="3528360748637781274">"Ajuda e comentários"</string>
+ <string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Armazenamento"</string>
<string name="shared_data_title" msgid="1017034836800864953">"Dados partilhados"</string>
<string name="shared_data_summary" msgid="5516326713822885652">"Ver e modificar dados partilhados"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Uma barra de telefone."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Duas barras de telefone."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Três barras de telefone."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Sinal de telefone completo."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Sem dados."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Uma barra de dados."</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 811c04a..f37764a 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Ativo, apenas o esquerdo"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Ativo, apenas o direito"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Ativo, esquerdo e direito"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Ativo (apenas mídia), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Ativo (apenas mídia), E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Conectado (com suporte ao compartilhamento de áudio), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Conectado (com suporte ao compartilhamento de áudio), E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Conectado (com suporte ao compartilhamento de áudio), esquerdo <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Conectado (com suporte ao compartilhamento de áudio), direito <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Ativo (apenas mídia)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Com suporte ao compartilhamento de áudio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Ativo (apenas mídia), apenas esquerdo"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Ativo (apenas mídia), somente direito"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Ativo (apenas mídia), esquerdo e direito"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Áudio da mídia"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Chamadas telefônicas"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência de arquivo"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a conclusão"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (carregando)"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Carga completa"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Carregamento suspenso"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada pelo admin"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlada pelas configurações restritas"</string>
<string name="disabled" msgid="8017887509554714950">"Desativado"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Uma barra de sinal do telefone."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Duas barras de sinal do telefone."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Três barras de sinal do telefone."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Sinal do telefone cheio."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Nenhum dado."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Uma barra de sinal de dados."</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 1233afd..e7101eb 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Activ, numai stânga"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Activ, numai dreapta"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Activ, stânga și dreapta"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Activ (numai pentru conținut media), nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Activ (numai pentru conținut media): nivelul bateriei din stânga: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, nivelul bateriei din dreapta: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Conectat (acceptă permiterea accesului la audio), nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Conectat (acceptă permiterea accesului la audio), nivelul bateriei din stânga: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, nivelul bateriei din dreapta: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Conectat (acceptă permiterea accesului la audio), nivelul bateriei din stânga: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Conectat (acceptă permiterea accesului la audio), nivelul bateriei din dreapta: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Activ (numai pentru conținut media)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Acceptă permiterea accesului la audio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Activ (numai pentru conținut media), numai stânga"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Activ (numai pentru conținut media), numai dreapta"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Activ (numai pentru conținut media), stânga și dreapta"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Conținut media audio"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Apeluri telefonice"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfer de fișiere"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> până la finalizare"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Încărcare optimizată"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Se încarcă"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Necunoscut"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Se încarcă"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Se încarcă rapid"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Încărcată"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Complet încărcată"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Încărcare întreruptă"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlată de administrator"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlată de setarea restricționată"</string>
<string name="disabled" msgid="8017887509554714950">"Dezactivată"</string>
@@ -553,7 +554,7 @@
<string name="okay" msgid="949938843324579502">"OK"</string>
<string name="done" msgid="381184316122520313">"Gata"</string>
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarme și mementouri"</string>
- <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Permite setarea pentru alarme și mementouri"</string>
+ <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Permite setarea de alarme și mementouri"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarme și mementouri"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite acestei aplicații să seteze alarme și să planifice acțiuni care trebuie realizate în timp scurt. Astfel, aplicația poate să ruleze în fundal, ceea ce ar putea crește consumul de baterie.\n\nDacă permisiunea este dezactivată, alarmele și evenimentele dependente de timp planificate de aplicație nu vor funcționa."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programare, alarmă, memento, ceas"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Semnal pentru telefon: o bară."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Semnal pentru telefon: două bare."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Semnal pentru telefon: trei bare."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Semnal pentru telefon: complet."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Nu există semnal pentru date."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Semnal pentru date: o bară."</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 43645f4..c77becf 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Активен, только левое ухо"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Активен, только правое ухо"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Активен, оба уха"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Используется (только для медиа), заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Используется (только для медиа), заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (Л), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (П)"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Подключено (поддерживается отправка аудио), заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Подключено (поддерживается отправка аудио), заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (Л), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (П)"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Подключено (поддерживается отправка аудио), заряд левого наушника: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Подключено (поддерживается отправка аудио), заряд правого наушника: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Используется (только для медиа)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Поддерживается отправка аудио"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Используется (только для медиа), левый наушник"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Используется (только для медиа), правый наушник"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Используется (только для медиа), левый и правый наушники"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Профиль A2DP"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Звонки"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Профиль OPP"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядка оптимизирована"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряжается"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Идет зарядка"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Быстрая зарядка"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Батарея заряжена"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Батарея заряжена"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Зарядка приостановлена"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Контролируется администратором"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролируется настройками с ограниченным доступом"</string>
<string name="disabled" msgid="8017887509554714950">"Отключено"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Сигнал телефонной сети: одно деление."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Сигнал телефонной сети: два деления."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Сигнал телефонной сети: три деления."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Надежный телефонный сигнал."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Сигнал передачи данных отсутствует."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Сигнал передачи данных: одно деление."</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index d965b73..2f72f01 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"සක්රිය, වම පමණි"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"සක්රිය, දකුණ පමණි"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"සක්රිය, වම සහ දකුණ"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"සක්රිය (මාධ්ය පමණි), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> බැටරිය"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"සක්රිය (මාධ්ය පමණි), ව: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> බැටරිය, ද: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> බැටරිය"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"සම්බන්ධයි (ශ්රව්ය බෙදා ගැනීම සහය දක්වයි), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> බැටරිය"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"සම්බන්ධයි (ශ්රව්ය බෙදා ගැනීම සහය දක්වයි), ව: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> බැටරිය, ද: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> බැටරිය"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"සම්බන්ධයි (ශ්රව්ය බෙදා ගැනීම සහය දක්වයි), වම <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"සම්බන්ධයි (ශ්රව්ය බෙදා ගැනීම සහය දක්වයි), දකුණ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"සක්රිය (මාධ්ය පමණි)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ශ්රව්ය බෙදා ගැනීම සහය දක්වයි"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"සක්රිය (මාධ්ය පමණි), වම පමණි"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"සක්රිය (මාධ්ය පමණි), දකුණ පමණි"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"සක්රිය (මාධ්ය පමණි), වම සහ දකුණ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"මාධ්ය ශ්රව්ය"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"දුරකථන ඇමතුම්"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ගොනු හුවමාරුව"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - සම්පූර්ණ වීමට <xliff:g id="TIME">%2$s</xliff:g>ක් ඉතිරියි"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය ප්රශස්ත කර ඇත"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය වේ"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"නොදනී"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ආරෝපණය වෙමින්"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්ර ආරෝපණය"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"අරෝපිතයි"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"සම්පූර්ණයෙන් ආරෝපණ වී ඇත"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ආරෝපණය රදවාගෙන ඇත"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"පරිපාලක විසින් පාලනය කරන ලදී"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"සීමා කළ සැකසීම මගින් පාලනය වේ"</string>
<string name="disabled" msgid="8017887509554714950">"අබල කර ඇත"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"දුරකථනය තීරු එකයි."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"දුරකථනය තීරු දෙකයි."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"දුරකථනය තීරු තුනයි."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"දුරකථනයේ සංඥාව පිරී ඇත."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"දත්ත නැත."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"දත්ත තීරු එකයි."</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 77c265e..9b143b7 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktívne, iba ľavá strana"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktívne, iba pravá strana"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktívne, ľavá aj pravá strana"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktívne (iba médiá), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batérie"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktívne (iba médiá), Ľ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batérie, P: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batérie"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Pripojené (podporuje zdieľanie zvuku), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batérie"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Pripojené (podporuje zdieľanie zvuku), Ľ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batérie, P: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batérie"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Pripojené (podporuje zdieľanie zvuku), ľavá strana: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Pripojené (podporuje zdieľanie zvuku), pravá strana: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktívne (iba médiá)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Podporuje zdieľanie zvuku"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktívne (iba médiá), iba ľavá strana"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktívne (iba médiá), iba pravá strana"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktívne (iba médiá), ľavá aj pravá strana"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Zvuk médií"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonické hovory"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos súborov"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Nabíjanie je optimalizované"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – nabíja sa"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Neznáme"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíja sa"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rýchle nabíjanie"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Nabité"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Úplne nabitá"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Nabíjanie je pozastavené"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Ovládané správcom"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ovládané obmedzeným nastavením"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktivované"</string>
@@ -555,7 +556,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Budíky a pripomenutia"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Povoliť nastavovanie budíkov a pripomenutí"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Budíky a pripomenutia"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Povoľte tejto aplikácii nastavovať budíky a plánovať akcie s časovým obmedzením. Aplikácii to umožní pracovať na pozadí, čo môže zvýšiť spotrebu batérie.\n\nAk je toto povolenie vypnuté, existujúce budíky a udalosti s časovým obmedzením naplánované touto aplikáciu nebudú fungovať."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Povoľte tejto aplikácii nastavovať budíky a plánovať akcie s časovým obmedzením. Aplikácii to umožní pracovať na pozadí, čo môže zvýšiť spotrebu batérie.\n\nAk je toto povolenie vypnuté, existujúce budíky a udalosti s časovým obmedzením naplánované touto aplikáciou nebudú fungovať."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"plán, budík, pripomenutie, hodiny"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Zapnúť"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Zapnite režim bez vyrušení"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Jeden stĺpec signálu telefónnej siete."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Dve čiarky signálu telefónnej siete."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Tri čiarky signálu telefónnej siete."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Plný signál telefónnej siete."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Žiadna dátová sieť."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Jedna čiarka signálu dátovej siete."</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index abca26f..6e3f21b 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktivno, samo levo"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktivno, samo desno"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktivno, levo in desno"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktivno (samo predstavnost), napolnjenost baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktivno (samo predstavnost), napolnjenost leve baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, napolnjenost desne baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Povezano (podpira deljenje zvoka), napolnjenost baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Povezano (podpira deljenje zvoka), napolnjenost leve baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, napolnjenost desne baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Povezano (podpira deljenje zvoka), napolnjenost leve baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Povezano (podpira deljenje zvoka), napolnjenost desne baterije: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktivno (samo predstavnost)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Podpira deljenje zvoka"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktivno (samo predstavnost), samo levo"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktivno (samo predstavnost), samo desno"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktivno (samo predstavnost), levo in desno"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Zvok predstavnosti"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonski klici"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos datoteke"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – še <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – polnjenje je optimizirano"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – polnjenje"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Neznano"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Polnjenje"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Napolnjeno"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Popolnoma napolnjena"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Polnjenje je na čakanju"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Nadzira skrbnik"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Pod nadzorom omejene nastavitve"</string>
<string name="disabled" msgid="8017887509554714950">"Onemogočeno"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefon z eno črtico."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefon z dvema črticama."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefon s tremi črticami."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Signal telefona je poln."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Ni podatkov."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Podatkovni signal z eno črtico."</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 0f48e42c9..82b3cb2 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktive, vetëm majtas"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktive, vetëm djathtas"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktive, majtas dhe djathtas"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktiv (vetëm për media), bateria në <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktiv (vetëm për media), majtas: bateria në <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, djathtas: bateria në <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Lidhur (mbështet ndarjen e audios), bateria në <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Lidhur (mbështet ndarjen e audios), majtas: bateria në <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, djathtas: bateria në <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Lidhur (mbështet ndarjen e audios), majtas <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Lidhur (mbështet ndarjen e audios), djathtas <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktiv (vetëm për media)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Mbështet ndarjen e audios"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktiv (vetëm për media), vetëm majtas"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktiv (vetëm për media), vetëm djathtas"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktiv (vetëm për media), majtas dhe djathtas"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Audioja e medias"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonatat"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferimi i skedarëve"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të mbushet"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi u optimizua"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Po karikohet"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"I panjohur"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Po karikohet"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Karikim i shpejtë"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Karikuar"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Karikuar plotësisht"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Karikimi në pritje"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolluar nga administratori"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrollohet nga \"Cilësimet e kufizuara\""</string>
<string name="disabled" msgid="8017887509554714950">"Çaktivizuar"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefoni ka edhe një vijë."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefoni ka dy vija."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefoni ka tre vija."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Sinjali i telefonit është i plotë."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Nuk ka të dhëna."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Sinjali është vetëm një vijë."</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index d309036c..0437a14 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Активно, само с леве стране"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Активно, с десне стране"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Активно, с леве и десне стране"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Активан (само за медије), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батерије"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Активан (само за медије), лево: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> батерије, десно: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батерије"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Повезан (подржава дељење звука), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батерије"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Повезан (подржава дељење звука), лево: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> батерије, десно: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батерије"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Повезан (подржава дељење звука), лево <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Повезан (подржава дељење звука), десно <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Активан (само за медије)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Подржава дељење звука"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Активан (само за медије), само лево"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Активан (само за медије), само десно"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Активан (само за медије), лево и десно"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Звук медија"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Телефонски позиви"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Пренос датотеке"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до краја пуњења"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – пуњење је оптимизовано"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Пуњење"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Пуни се"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Напуњено"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Напуњено до краја"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Пуњење је на чекању"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Контролише администратор"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролишу ограничена подешавања"</string>
<string name="disabled" msgid="8017887509554714950">"Онемогућено"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Сигнал телефона има једну црту."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Сигнал телефона од две црте."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Сигнал телефона од три црте."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Сигнал телефона је пун."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Нема података."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Сигнал за податке има једну црту."</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 363396a..5e6db36 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktiv, bara vänster"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktiv, bara höger"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktiv, vänster och höger"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktiv (endast media), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktiv (endast media), V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Ansluten (ljuddelning stöds), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Ansluten (ljuddelning stöds), V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Ansluten (ljuddelning stöds), vänster <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Ansluten (ljuddelning stöds), höger <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktiv (endast media)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Ljuddelning stöds"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktiv (endast media), endast vänster"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktiv (endast media), endast höger"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktiv (endast media), vänster och höger"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Medialjud"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonsamtal"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filöverföring"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kvar tills fulladdat"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Laddningen har optimerats"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – laddas"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Okänd"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laddar"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Laddat"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fulladdad"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Laddningen har pausats"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Strys av administratören"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Styrs av spärrad inställning"</string>
<string name="disabled" msgid="8017887509554714950">"Inaktiverad"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefon: en stapel."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefon: två staplar."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefon: tre staplar."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefonsignalen är full."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Inga data."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data: en stapel."</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index ebff38b..f8307e2 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Inatumika, kushoto pekee"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Inatumika, kulia pekee"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Inatumika, kushoto na kulia"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Inatumika (maudhui pekee), chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Inatumika (maudhui pekee), Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja), chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja), Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja), chaji ya betri ya kushoto imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja), chaji ya betri ya kulia imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Inatumika (maudhui pekee)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Inaweza kutumia kipengele cha kusikiliza pamoja"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Inatumika (maudhui pekee), kushoto pekee"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Inatumika (maudhui pekee), kulia pekee"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Inatumika (maudhui pekee), kushoto na kulia"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Sauti ya maudhui"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Simu"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Uhamishaji wa faili"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> zimesalia ijae chaji"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Hali ya kuchaji imeboreshwa"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Inachaji"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Haijulikani"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Inachaji"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Inachaji kwa kasi"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Imechajiwa"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Imejaa Chaji"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Imesitisha kuchaji"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Imedhibitiwa na msimamizi"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Imedhibitiwa na Mpangilio wenye Mipaka"</string>
<string name="disabled" msgid="8017887509554714950">"Imezimwa"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Mwambaa mmoja wa simu."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Miambaa miwili ya simu"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Miambaa mitatu ya simu."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Ishara ya simu imejaa."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Hakuna data."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Upapi mmoja wa habari"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 1648de0..c6896a2 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"இடது பக்கம் மட்டும் செயலில் உள்ளது"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"வலது பக்கம் மட்டும் செயலில் உள்ளது"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"வலது மற்றும் இடது பக்கம் செயலில் உள்ளது"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"செயலிலுள்ளது (மீடியா மட்டும்), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> பேட்டரி"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"செயலிலுள்ளது (மீடியா மட்டும்), இடது: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> பேட்டரி, வலது: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> பேட்டரி"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"இணைக்கப்பட்டுள்ளது (ஆடியோ பகிர்வை ஆதரிக்கிறது), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> பேட்டரி"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"இணைக்கப்பட்டுள்ளது (ஆடியோ பகிர்வை ஆதரிக்கிறது), இடது: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> பேட்டரி, வலது: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> பேட்டரி"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"இணைக்கப்பட்டுள்ளது (ஆடியோ பகிர்வை ஆதரிக்கிறது), இடது: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"இணைக்கப்பட்டுள்ளது (ஆடியோ பகிர்வை ஆதரிக்கிறது), வலது: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"செயலிலுள்ளது (மீடியா மட்டும்)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ஆடியோ பகிர்வை ஆதரிக்கிறது"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"செயலிலுள்ளது (மீடியா மட்டும்), இடதுபுறம் மட்டும்"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"செயலிலுள்ளது (மீடியா மட்டும்), வலதுபுறம் மட்டும்"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"செயலிலுள்ளது (மீடியா மட்டும்), இடதுபுறம் மற்றும் வலதுபுறம்"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"மீடியா ஆடியோ"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ஃபோன் அழைப்புகள்"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ஃபைல் இடமாற்றம்"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழுவதும் சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - சார்ஜிங் மேம்படுத்தப்பட்டது"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ சார்ஜாகிறது"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"அறியப்படாத"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"சார்ஜ் ஆகிறது"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"வேகமாக சார்ஜாகிறது"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"சார்ஜாகிவிட்டது"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"முழுவதும் சார்ஜாகிவிட்டது"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"சார்ஜிங் இடைநிறுத்தப்பட்டுள்ளது"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"நிர்வாகி கட்டுப்படுத்துகிறார்"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"வரையறுக்கப்பட்ட அமைப்பால் கட்டுப்படுத்தப்படுகிறது"</string>
<string name="disabled" msgid="8017887509554714950">"முடக்கப்பட்டது"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"சிக்னல் ஒரு கோட்டில் உள்ளது."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"சிக்னல் இரண்டு கோட்டில் உள்ளது."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"சிக்னல் மூன்று கோட்டில் உள்ளது."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"சிக்னல் முழுமையாக உள்ளது."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"டேட்டா சிக்னல் இல்லை."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"தரவு சிக்னல் ஒரு கோட்டில் உள்ளது."</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 7ee57cf..c61cf2e 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"యాక్టివ్గా ఉంది, ఎడమవైపు మాత్రమే యాక్టివ్గా ఉంది"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"యాక్టివ్గా ఉంది, కుడివైపు యాక్టివ్గా ఉంది"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"యాక్టివ్గా ఉంది, ఎడమవైపు, కుడివైపు యాక్టివ్గా ఉంది"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"యాక్టివ్ (మీడియా మాత్రమే), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> బ్యాటరీ"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"యాక్టివ్ (మీడియా మాత్రమే), ఎడమ వైపు: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> బ్యాటరీ, కుడివైపు: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> బ్యాటరీ"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"కనెక్ట్ చేయబడింది (ఆడియో షేరింగ్కు సపోర్ట్ చేస్తుంది), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> బ్యాటరీ"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"కనెక్ట్ చేయబడింది (ఆడియో షేరింగ్కు సపోర్ట్ చేస్తుంది), ఎడమ వైపు: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> బ్యాటరీ, కుడివైపు: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> బ్యాటరీ"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"కనెక్ట్ చేయబడింది (ఆడియో షేరింగ్కు సపోర్ట్ చేస్తుంది), ఎడమ వైపు <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"కనెక్ట్ చేయబడింది (ఆడియో షేరింగ్కు సపోర్ట్ చేస్తుంది), కుడివైపు <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"యాక్టివ్ (మీడియా మాత్రమే)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ఆడియో షేరింగ్కు సపోర్ట్ చేస్తుంది"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"యాక్టివ్ (మీడియా మాత్రమే), ఎడమ వైపు మాత్రమే"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"యాక్టివ్ (మీడియా మాత్రమే), కుడివైపు మాత్రమే"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"యాక్టివ్ (మీడియా మాత్రమే), ఎడమ, కుడివైపు మాత్రమే"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"మీడియా ఆడియో"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ఫోన్ కాల్స్"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ఫైల్ బదిలీ"</string>
@@ -162,8 +151,8 @@
<string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ఇన్పుట్ కోసం ఉపయోగించండి"</string>
<string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"వినికిడి పరికరాల కోసం ఉపయోగించండి"</string>
<string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO కోసం ఉపయోగించండి"</string>
- <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"జత చేయి"</string>
- <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"జత చేయి"</string>
+ <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"పెయిర్ చేయండి"</string>
+ <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"పెయిర్ చేయండి"</string>
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"రద్దు చేయండి"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"పెయిర్ చేయడం వలన కనెక్ట్ చేయబడినప్పుడు మీ కాంటాక్ట్లకు అలాగే కాల్ హిస్టరీకి యాక్సెస్ను మంజూరు చేస్తుంది."</string>
<string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో జత చేయడం సాధ్యపడలేదు."</string>
@@ -201,7 +190,7 @@
<string name="running_process_item_user_label" msgid="3988506293099805796">"యూజర్: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"కొన్ని ఆటోమేటిక్ సెట్టింగ్లు సెట్ చేయబడ్డాయి"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"ఆటోమేటిక్ ఆప్షన్లు ఏవీ సెట్ చేయలేదు"</string>
- <string name="tts_settings" msgid="8130616705989351312">"వచనం నుండి ప్రసంగం సెట్టింగ్లు"</string>
+ <string name="tts_settings" msgid="8130616705989351312">"టెక్స్ట్-టు-స్పీచ్ సెట్టింగ్లు"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"టెక్స్ట్-టు-స్పీచ్ అవుట్పుట్"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"స్పీచ్ రేట్"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"వచనాన్ని చదివి వినిపించాల్సిన వేగం"</string>
@@ -339,7 +328,7 @@
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"లాగ్ బఫర్కి లాగర్ పరిమా. ఎంచుకోండి"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"లాగర్ నిరంతర నిల్వలోని డేటాను తీసివేయాలా?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"మేము నిరంతర లాగర్తో ఇక పర్యవేక్షించనప్పుడు, మీ పరికరంలోని లాగర్ డేటాను మేము తొలగించాల్సి ఉంటుంది."</string>
- <string name="select_logpersist_title" msgid="447071974007104196">"పరికరంలో లాగర్ డేటా నిరంతరం స్టోర్ చేయి"</string>
+ <string name="select_logpersist_title" msgid="447071974007104196">"పరికరంలో లాగర్ డేటా నిరంతరం స్టోర్ చేయండి"</string>
<string name="select_logpersist_dialog_title" msgid="7745193591195485594">"పరికరంలో నిరంతరం స్టోరేజ్ చేయాల్సిన లాగ్ బఫర్లను ఎంచుకోండి"</string>
<string name="select_usb_configuration_title" msgid="6339801314922294586">"USB కాన్ఫిగరేషన్ని ఎంచుకోండి"</string>
<string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"USB కాన్ఫిగరేషన్ని ఎంచుకోండి"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జింగ్ ఆప్టిమైజ్ చేయబడింది"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ అవుతోంది"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"తెలియదు"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ఛార్జ్ అవుతోంది"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"ఛార్జ్ చేయబడింది"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"పూర్తి ఛార్జ్ అయింది"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ఛార్జింగ్ హోల్డ్లో ఉంది"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"నిర్వాహకుని ద్వారా నియంత్రించబడింది"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"పరిమితం చేసిన సెట్టింగ్ ద్వారా నియంత్రించబడుతుంది"</string>
<string name="disabled" msgid="8017887509554714950">"డిజేబుల్ చేయబడింది"</string>
@@ -555,7 +556,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"అలారాలు, రిమైండర్లు"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"అలారాలు, రిమైండర్లను సెట్ చేయడానికి అనుమతించండి"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"అలారాలు & రిమైండర్లు"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"అలారాలను సెట్ చేయడానికి, సమయ-సునిశిత చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. ఇది యాప్ను బ్యాక్గ్రౌండ్లో రన్ అవడానికి అనుమతిస్తుంది, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు.\n\nఈ అనుమతిని ఆఫ్ చేస్తే, ఈ యాప్ ద్వారా షెడ్యూల్ చేసిన ఇప్పటికే ఉన్న అలారాలు, సమయ-ఆధారిత ఈవెంట్లు పనిచేయవు."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"అలారాలను సెట్ చేయడానికి, టైమ్-సెన్సిటివ్ చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. ఇది యాప్ను బ్యాక్గ్రౌండ్లో రన్ అవడానికి అనుమతిస్తుంది, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు.\n\nఈ అనుమతిని ఆఫ్ చేస్తే, ఈ యాప్ ద్వారా షెడ్యూల్ చేసినటువంటి ఇప్పటికే ఉన్న అలారాలు, టైమ్-ఆధారిత ఈవెంట్లు పనిచేయవు."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"షెడ్యూల్, అలారం, రిమైండర్, గడియారం"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ఆన్ చేయండి"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"అంతరాయం కలిగించవద్దును ఆన్ చేయండి"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"ఫోన్ ఒక బారు."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"ఫోన్ రెండు బార్లు."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"ఫోన్ మూడు బార్లు."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"ఫోన్ సిగ్నల్ పూర్తిగా ఉంది."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"డేటా లేదు."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"డేటా ఒక బారు."</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 55b57db..74087e5 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"ใช้งานอยู่ เฉพาะข้างซ้าย"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"ใช้งานอยู่ เฉพาะข้างขวา"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"ใช้งานอยู่ ข้างซ้ายและขวา"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"ใช้งานอยู่ (สื่อเท่านั้น), แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"ใช้งานอยู่ (สื่อเท่านั้น), L: แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"เชื่อมต่อแล้ว (รองรับการแชร์เสียง), แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"เชื่อมต่อแล้ว (รองรับการแชร์เสียง), L: แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"เชื่อมต่อแล้ว (รองรับการแชร์เสียง), ซ้าย <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"เชื่อมต่อแล้ว (รองรับการแชร์เสียง), ขวา <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"ใช้งานอยู่ (สื่อเท่านั้น)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"รองรับการแชร์เสียง"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"ใช้งานอยู่ (สื่อเท่านั้น), ซ้ายเท่านั้น"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"ใช้งานอยู่ (สื่อเท่านั้น), ขวาเท่านั้น"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"ใช้งานอยู่ (สื่อเท่านั้น), ซ้ายและขวา"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"เสียงของสื่อ"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"โทรศัพท์"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"การถ่ายโอนไฟล์"</string>
@@ -203,7 +192,7 @@
<string name="launch_defaults_none" msgid="8049374306261262709">"ไม่ได้ตั้งค่าเริ่มต้น"</string>
<string name="tts_settings" msgid="8130616705989351312">"การตั้งค่าการอ่านออกเสียงข้อความ"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"เอาต์พุตการอ่านออกเสียงข้อความ"</string>
- <string name="tts_default_rate_title" msgid="3964187817364304022">"ความเร็วของคำพูด"</string>
+ <string name="tts_default_rate_title" msgid="3964187817364304022">"ความเร็วในการพูด"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"ความเร็วในการพูดข้อความ"</string>
<string name="tts_default_pitch_title" msgid="6988592215554485479">"ความสูง-ต่ำของเสียง"</string>
<string name="tts_default_pitch_summary" msgid="9132719475281551884">"มีผลต่อโทนเสียงของข้อความสังเคราะห์"</string>
@@ -487,7 +476,7 @@
<string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g> เมื่อดูจากการใช้งานของคุณ"</string>
<string name="power_discharge_by" msgid="4113180890060388350">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_discharge_by_only_short" msgid="5883041507426914446">"จนถึง <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="5883041507426914446">"จนถึง <xliff:g id="TIME">%1$s</xliff:g> น."</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"แบตเตอรี่อาจหมดภายใน <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"เหลือน้อยกว่า <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"เหลือน้อยกว่า <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - อีก <xliff:g id="TIME">%2$s</xliff:g> จึงจะเต็ม"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ปรับการชาร์จให้เหมาะสมแล้ว"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ กำลังชาร์จ"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ไม่ทราบ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"กำลังชาร์จ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"กำลังชาร์จอย่างเร็ว"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"ชาร์จแล้ว"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"ชาร์จเต็มแล้ว"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"หยุดการชาร์จชั่วคราว"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ผู้ดูแลระบบเป็นผู้ควบคุม"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ควบคุมโดยการตั้งค่าที่จำกัด"</string>
<string name="disabled" msgid="8017887509554714950">"ปิดอยู่"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"สัญญาณโทรศัพท์หนึ่งขีด"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"สัญญาณโทรศัพท์สองขีด"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"สัญญาณโทรศัพท์สามขีด"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"สัญญาณโทรศัพท์เต็ม"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"ไม่มีข้อมูล"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"สัญญาณข้อมูลหนึ่งขีด"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index c34588d..aaa2bd0 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktibo, kaliwa lang"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktibo, kanan lang"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktibo, kaliwa at kanan"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Aktibo (media lang), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterya"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Aktibo (media lang), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> baterya, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterya"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Nakakonekta (sinusuportahan ang pag-share ng audio), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterya"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Nakakonekta (sinusuportahan ang pag-share ng audio), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> baterya, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterya"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Nakakonekta (sinusuportahan ang pag-share ng audio), kaliwa <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Nakakonekta (sinusuportahan ang pag-share ng audio), kanan <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Aktibo (media lang)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Sinusuportahan ang pag-share ng audio"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Aktibo (media lang), kaliwa lang"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Aktibo (media lang), kanan lang"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Aktibo (media lang), kaliwa at kanan"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Audio ng media"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Mga tawag sa telepono"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Paglilipat ng file"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> na lang bago mapuno"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Naka-optimize ang pag-charge"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Nagcha-charge"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Hindi Kilala"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Nagcha-charge"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mabilis na charge"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Puno ang Baterya"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Naka-hold ang pag-charge"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Pinapamahalaan ng admin"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kinokontrol ng Pinaghihigpitang Setting"</string>
<string name="disabled" msgid="8017887509554714950">"Naka-disable"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telepono na isang bar."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telepono na dalawang bar."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telepono na tatlong bar."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Puno ang signal ng telepono."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Walang data."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Data na isang bar."</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 4315241..bf990c7 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Yalnızca sol tarafta etkin"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Yalnızca sağ tarafta etkin"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Sol ve sağ tarafta etkin"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Etkin (yalnızca medya), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pil seviyesi"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Etkin (yalnızca medya), Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> pil seviyesi, Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> pil seviyesi"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Bağlı (ses paylaşımını destekler), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pil seviyesi"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Bağlı (ses paylaşımını destekler), Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> pil seviyesi, Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> pil seviyesi"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Bağlı (ses paylaşımını destekler), sol <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Bağlı (ses paylaşımını destekler), sağ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Etkin (yalnızca medya)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Ses paylaşımını destekler"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Etkin (yalnızca medya), yalnızca sol"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Etkin (yalnızca medya), yalnızca sağ"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Etkin (yalnızca medya), sol ve sağ"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Medya sesi"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefon aramaları"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dosya aktarımı"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj işlemi optimize edildi"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Şarj ediliyor"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hızlı şarj oluyor"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Şarj oldu"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Pilin Şarjı Tam"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Şarj işlemi beklemede"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Yönetici tarafından denetleniyor"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kısıtlanmış ayar tarafından kontrol ediliyor"</string>
<string name="disabled" msgid="8017887509554714950">"Devre dışı"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefon sinyali bir çubuk."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefon sinyali iki çubuk."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefon sinyali üç çubuk."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefon sinyali tam."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Veri yok."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Veri sinyali bir çubuk."</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index ac9edc5..d945b5f 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Активовано, лише лівий"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Активовано, лише правий"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Активовано, лівий і правий"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Активно (лише для мультимедіа); <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> заряду акумулятора"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Активно (лише для мультимедіа); лівий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> заряду акумулятора, правий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> заряду акумулятора"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Підключено (підтримує надсилання аудіо); <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> заряду акумулятора"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Підключено (підтримує надсилання аудіо); лівий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> заряду акумулятора, правий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> заряду акумулятора"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Підключено (підтримує надсилання аудіо); лівий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Підключено (підтримує надсилання аудіо); правий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Активно (лише для мультимедіа)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Підтримує надсилання аудіо"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Активно (лише для мультимедіа); лише лівий"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Активно (лише для мультимедіа); лише правий"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Активно (лише для мультимедіа); лівий і правий"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Звук медіафайлів"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Телефонні дзвінки"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Передавання файлів"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджання оптимізовано"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджається"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Невідомо"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Заряджається"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Заряджено"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Повністю заряджено"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Заряджання призупинено"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Керується адміністратором"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Керується налаштуваннями з обмеженнями"</string>
<string name="disabled" msgid="8017887509554714950">"Вимкнено"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Одна смужка сигналу телефону."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Дві смужки сигналу телефону."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Три смужки сигналу телефону."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Максимальний сигнал телефону."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Немає сигналу даних."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Одна смужка сигналу даних."</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index d2fc32a..ab97d02 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"فعال، صرف بائیں طرف"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"فعال، صرف دائیں طرف"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"فعال، صرف بائیں اور دائیں طرف"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"فعال (صرف میڈیا)، <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"فعال (صرف میڈیا)، L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> بیٹری، R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> بیٹری"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)، <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)، L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> بیٹری، R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> بیٹری"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)، بائیں <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)، دائیں <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"فعال (صرف میڈیا)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"آڈیو کے اشتراک کو سپورٹ کرتا ہے"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"فعال (صرف میڈیا)، صرف بائیں"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"فعال (صرف میڈیا)، صرف دائیں"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"فعال (صرف میڈیا)، بائیں اور دائیں"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"میڈيا آڈیو"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"فون کالز"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"فائل کی منتقلی"</string>
@@ -487,7 +476,7 @@
<string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"آپ کے استعمال کی بنیاد پر تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی"</string>
<string name="power_discharge_by" msgid="4113180890060388350">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی"</string>
- <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> تک"</string>
+ <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> تک"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"<xliff:g id="TIME">%1$s</xliff:g> تک بیٹری ختم ہو سکتی ہے"</string>
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"مکمل چارج ہونے میں <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> باقی ہے"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارجنگ کو بہتر بنایا گیا"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارج ہو رہی ہے"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"نامعلوم"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"چارج ہو رہا ہے"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"تیزی سے چارج ہو رہا ہے"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"چارج ہو گئی"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"مکمل طور پر چارج ہو گئی"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"چارجنگ ہولڈ پر ہے"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"کنٹرول کردہ بذریعہ منتظم"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"محدود کردہ ترتیب کے زیر انتظام ہے"</string>
<string name="disabled" msgid="8017887509554714950">"غیر فعال"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"فون کا ایک بار۔"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"فون کے دو بارز۔"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"فون کے تین بارز۔"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"فون سگنل پورا ہے۔"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"کوئی ڈیٹا نہیں ہے۔"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"ڈیٹا کا ایک بار۔"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 3c8e249..0bfa951 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Faol, faqat chap"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Faol, faqat oʻng"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Faol, chap va oʻng"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Faol (faqat media uchun), quvvat: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Faol (faqat media uchun), quvvat: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (L), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (R)"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Ulangan (audio yuborish mumkin), quvvat: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Ulangan (audio yuborish mumkin), quvvat: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (L), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (R)"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Ulangan (audio yuborish mumkin), quvvat: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (chap)"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Ulangan (audio yuborish mumkin), quvvat: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (oʻng)"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Faol (faqat media uchun)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Audio yuborishi mumkin"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Faol (faqat media uchun), faqat chap"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Faol (faqat media uchun), faqat oʻng"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Faol (faqat media uchun), chap va oʻng"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"A2DP profili"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefon chaqiruvlari"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fayl uzatish"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Toʻlishiga <xliff:g id="TIME">%2$s</xliff:g> qoldi"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlash optimallashtirildi"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlanmoqda"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Noma’lum"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Quvvat olmoqda"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Quvvat oldi"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Toʻliq quvvatlandi"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Quvvatlash toʻxtatildi"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Administrator tomonidan boshqariladi"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Cheklangan sozlama tomonidan boshqariladi"</string>
<string name="disabled" msgid="8017887509554714950">"Oʻchiq"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Telefon bitta panelda."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Telefon ikkita panelda."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Telefon uchta panelda."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Telefon signali to‘liq."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Ma’lumotlar yo‘q."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Ma’lumotlar bitta panelda."</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index b3d70cc..d0062d45 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Đang hoạt động, chỉ tai bên trái"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Đang hoạt động, chỉ tai phải"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Đang hoạt động, cả tai phải và tai trái"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Đang hoạt động (chỉ phát nội dung đa phương tiện), pin còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Đang hoạt động (chỉ phát nội dung đa phương tiện), L: pin còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: pin còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Đã kết nối (có hỗ trợ tính năng chia sẻ âm thanh), pin còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Đã kết nối (có hỗ trợ tính năng chia sẻ âm thanh), L: pin còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: pin còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Đã kết nối (có hỗ trợ tính năng chia sẻ âm thanh), tai nghe bên trái còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pin"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Đã kết nối (có hỗ trợ tính năng chia sẻ âm thanh), tai nghe bên phải còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pin"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Đang hoạt động (chỉ phát nội dung đa phương tiện)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Hỗ trợ tính năng chia sẻ âm thanh"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Đang hoạt động (chỉ phát nội dung đa phương tiện), chỉ dùng tai nghe bên trái"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Đang hoạt động (chỉ phát nội dung đa phương tiện), chỉ dùng tai nghe bên phải"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Đang hoạt động (chỉ phát nội dung đa phương tiện), đang dùng cả tai nghe bên trái và phải"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Âm thanh nội dung nghe nhìn"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Cuộc gọi điện thoại"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Chuyển tệp"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là pin đầy"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quá trình sạc được tối ưu hoá"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đang sạc"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Không xác định"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Đang sạc"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Đang sạc nhanh"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Đã sạc"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Đã sạc đầy"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Đang tạm ngưng sạc"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Do quản trị viên kiểm soát"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Do chế độ Cài đặt hạn chế kiểm soát"</string>
<string name="disabled" msgid="8017887509554714950">"Đã tắt"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Tín hiệu điện thoại một vạch."</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Tín hiệu điện thoại hai vạch."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Tín hiệu điện thoại ba vạch."</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Tín hiệu điện thoại đầy đủ."</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Không có dữ liệu."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Tín hiệu dữ liệu một vạch."</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 6a9a18c..a291ede 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"使用中,仅左耳助听器"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"使用中,仅右耳助听器"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"使用中,左右耳助听器"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"使用中(仅限媒体),电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"使用中(仅限媒体),左侧电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>,右侧电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"已连接(支持音频分享),电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"已连接(支持音频分享),左侧电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>,右侧电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"已连接(支持音频分享),左侧电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"已连接(支持音频分享),右侧电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"使用中(仅限媒体)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"支持音频分享"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"使用中(仅限媒体),仅左侧"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"使用中(仅限媒体),仅右侧"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"使用中(仅限媒体),左侧和右侧"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"媒体音频"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"通话"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"文件传输"</string>
@@ -324,7 +313,7 @@
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"正在流式传输:<xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="7887550926056143018">"专用 DNS"</string>
<string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"选择专用 DNS 模式"</string>
- <string name="private_dns_mode_off" msgid="7065962499349997041">"已关闭"</string>
+ <string name="private_dns_mode_off" msgid="7065962499349997041">"关闭"</string>
<string name="private_dns_mode_opportunistic" msgid="1947864819060442354">"自动"</string>
<string name="private_dns_mode_provider" msgid="3619040641762557028">"专用 DNS 提供商主机名"</string>
<string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"输入 DNS 提供商的主机名"</string>
@@ -472,7 +461,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"红色弱视(红绿不分)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"蓝色弱视(蓝黄不分)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"色彩校正"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"“色彩校正”功能适用于以下情况:<br/> <ol> <li>您想更准确地查看颜色</li> <li>您想移除颜色以提高专注程度</li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"“色彩校正”功能适用于以下情况:<br/> <ol> <li>您想更准确地查看颜色</li> <li>您想移除颜色以提高专注度</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 为保护电池,已暂停充电"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充电方式已优化"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - 正在充电"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"正在充电"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充电"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"已充满电"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"已充满电"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"充电已暂停"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"由管理员控制"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由受限设置控制"</string>
<string name="disabled" msgid="8017887509554714950">"已停用"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"手机信号强度为一格。"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"手机信号强度为两格。"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"手机信号强度为三格。"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"手机信号满格。"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"没有数据网络信号。"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"数据信号强度为一格。"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index ecdfa62..2054136 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"使用中,僅左耳"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"使用中,僅右耳"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"使用中,左右耳"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"啟用 (只限媒體),<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> 電量"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"啟用 (只限媒體),左側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> 電量,右側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> 電量"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"已連線 (支援音訊分享功能),<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> 電量"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"已連線 (支援音訊分享功能),左側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> 電量,右側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> 電量"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"已連線 (支援音訊分享功能),左側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"已連線 (支援音訊分享功能),右側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"啟用 (只限媒體)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"支援音訊分享功能"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"左側啟用 (只限媒體)"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"右側啟用 (只限媒體)"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"右側同時啟用 (只限媒體)"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"媒體音效"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"通話"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"檔案傳輸"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充滿電"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 已優化充電"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ 充電中"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"已充滿電"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"充電完成"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"目前暫停充電"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"已由管理員停用"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由「受限設定」控制"</string>
<string name="disabled" msgid="8017887509554714950">"已停用"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"電話訊號強度為一格。"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"電話訊號強度為兩格。"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"電話訊號強度為三格。"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"電話訊號滿格。"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"沒有數據網絡。"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"數據網絡訊號強度為一格。"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 99fe41e..9996577 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"使用中,僅左耳"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"使用中,僅右耳"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"使用中,左右耳"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"啟用 (僅限媒體),<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> 剩餘電力"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"啟用 (僅限媒體),左側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> 剩餘電力,右側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> 剩餘電力"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"已連線 (支援音訊分享),<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> 剩餘電力"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"已連線 (支援音訊分享),左側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> 剩餘電力,右側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> 剩餘電力"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"已連線 (支援音訊分享),左側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"已連線 (支援音訊分享),右側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"啟用 (僅限媒體)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"支援音訊分享"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"左側啟用 (僅限媒體)"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"右側啟用 (僅限媒體)"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"左右側同時啟用 (僅限媒體)"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"媒體音訊"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"通話"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"檔案傳輸"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電效能已最佳化"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電中"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"充電完成"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"充電完成"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"目前暫停充電"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"已由管理員停用"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由受限制的設定控管"</string>
<string name="disabled" msgid="8017887509554714950">"已停用"</string>
@@ -555,7 +556,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"鬧鐘與提醒"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"允許設定鬧鐘和提醒"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"鬧鐘和提醒"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允許這個應用程式設定鬧鐘及安排有時效性的動作。這麼做會讓用程式在背景執行,可能比較耗電。\n\n如果關閉這項權限,這個應用程式設定的現有鬧鐘將不會響起,而且應用程式也無法在預定的時間發出活動提醒。"</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允許這個應用程式設定鬧鐘及安排有時效性的動作。之後應用程式可以在背景執行,並可能耗用較多電量。\n\n如果關閉這項權限,這個應用程式設定的現有鬧鐘將不會響起,系統也無法在預定的時間發出活動提醒。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"時間表, 鬧鐘, 提醒, 時鐘"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"開啟"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"開啟「零打擾」模式"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"電話訊號強度一格。"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"電話訊號強度兩格。"</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"電話訊號強度三格。"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"電話訊號滿格。"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"沒有數據網路。"</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"數據網路訊號強度一格。"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 6f06e1f..3cac220 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -106,28 +106,17 @@
<string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Iyasebenza, ngakwesokunxele kuphela"</string>
<string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Iyasebenza, ngakwesokudla kuphela"</string>
<string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Iyasebenza, ngakwesokunxele nakwesokudla"</string>
- <!-- no translation found for bluetooth_active_media_only_battery_level (1164678961213251365) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_battery_level_untethered (1345174295097854560) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_lea_support (8580950145907305436) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_lea_support (8534816721698743015) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_left_lea_support (6605320955858788855) -->
- <skip />
- <!-- no translation found for bluetooth_battery_level_untethered_right_lea_support (5717356160322149355) -->
- <skip />
- <!-- no translation found for bluetooth_active_media_only_no_battery_level (71106861912593126) -->
- <skip />
- <!-- no translation found for bluetooth_saved_device_lea_support (7231323139968285768) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_active (1632152540901488645) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_right_active (3854140683042617230) -->
- <skip />
- <!-- no translation found for bluetooth_hearing_aid_media_only_left_and_right_active (1299913413062528417) -->
- <skip />
+ <string name="bluetooth_active_media_only_battery_level" msgid="1164678961213251365">"Ibhethri (imidiya kuphela), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> liyasebenza"</string>
+ <string name="bluetooth_active_media_only_battery_level_untethered" msgid="1345174295097854560">"Liyasebenza (imidiya kuphela), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ibhethri, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ibhethri"</string>
+ <string name="bluetooth_battery_level_lea_support" msgid="8580950145907305436">"Ixhunyiwe (isekela ukwabelana ngokuqoshiwe), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> webhethri"</string>
+ <string name="bluetooth_battery_level_untethered_lea_support" msgid="8534816721698743015">"Ixhunyiwe (isekela ukwabelana ngokuqoshiwe), L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> webhethri, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> webhethri"</string>
+ <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="6605320955858788855">"Ixhunyiwe (isekela ukwabelana ngokuqoshiwe), ngakwesokunxele ngu-<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="5717356160322149355">"Ixhunyiwe (isekela ukwabelana ngokuqoshiwe), ngakwesokudla ngu-<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Kuyasebenza (imidiya kuphela)"</string>
+ <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Isekela ukwabelana ngokuqoshiwe"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Iyasebenza (imidiya kuphela), ngakwesokunxele kuphela"</string>
+ <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Kuyasebenza (imidiya kuphela), ngakwesokudla kuphela"</string>
+ <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Kuyasebenza (imidiya kuphela), ngakwesokunxele nakwesokudla"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Umsindo wemidiya"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Amakholi efoni"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dlulisa ifayela"</string>
@@ -498,6 +487,14 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> okusele kuze kugcwale"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ukushaja kuthuthukisiwe"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"Iku-<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Iyashaja"</string>
+ <!-- no translation found for power_fast_charging_duration_v2 (3797735998640359490) -->
+ <skip />
+ <!-- no translation found for power_charging_duration_v2 (2938998284074003248) -->
+ <skip />
+ <!-- no translation found for power_remaining_charging_duration_only_v2 (5358176435722950193) -->
+ <skip />
+ <!-- no translation found for power_remaining_fast_charging_duration_only_v2 (6270950195810579563) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Akwaziwa"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Iyashaja"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ishaja ngokushesha"</string>
@@ -509,6 +506,10 @@
<string name="battery_info_status_full" msgid="1339002294876531312">"Kushajiwe"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Ishaje Ngokuphelele"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Ukushaja kumisiwe"</string>
+ <!-- no translation found for battery_info_status_charging_v2 (6118522107222245505) -->
+ <skip />
+ <!-- no translation found for battery_info_status_charging_fast_v2 (1825439848151256589) -->
+ <skip />
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kulawulwa umqondisi"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kulawulwe Isethingi Elikhawulelwe"</string>
<string name="disabled" msgid="8017887509554714950">"Akusebenzi"</string>
@@ -695,6 +696,8 @@
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Ibha eyodwa yefoni"</string>
<string name="accessibility_phone_two_bars" msgid="2531458337458953263">"Amabha amabilil efoni."</string>
<string name="accessibility_phone_three_bars" msgid="1523967995996696619">"Amabha amathathu efoni"</string>
+ <!-- no translation found for accessibility_phone_four_bars (4477202400261338403) -->
+ <skip />
<string name="accessibility_phone_signal_full" msgid="4302338883816077134">"Isiginali yefoni igcwele"</string>
<string name="accessibility_no_data" msgid="4563181886936931008">"Ayikho idatha."</string>
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Idatha enye yebha"</string>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 2bd4d02..ab04904 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -82,7 +82,6 @@
<dimen name="add_a_photo_circled_padding">6dp</dimen>
<dimen name="user_photo_size_in_user_info_dialog">112dp</dimen>
<dimen name="add_a_photo_icon_size_in_user_info_dialog">32dp</dimen>
- <dimen name="user_name_height_in_user_info_dialog">48sp</dimen>
<!-- Minimum increment between density scales. -->
<fraction name="display_density_min_scale_interval">9%</fraction>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7e6b004..fbbed92 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1148,6 +1148,16 @@
<!-- [CHAR_LIMIT=80] Label for battery charging future pause -->
<string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging</string>
+ <!-- [CHAR_LIMIT=40] Label for battery level when fast charging with duration. -->
+ <string name="power_fast_charging_duration_v2"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="status">%2$s</xliff:g> - Full by <xliff:g id="time">%3$s</xliff:g></string>
+ <!-- [CHAR_LIMIT=40] Label for battery level when non-fast charging with duration. -->
+ <string name="power_charging_duration_v2"><xliff:g id="level">%1$s</xliff:g> - Fully charged by <xliff:g id="time">%2$s</xliff:g></string>
+
+ <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging. -->
+ <string name="power_remaining_charging_duration_only_v2">Fully charged by <xliff:g id="time">%1$s</xliff:g></string>
+ <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging. -->
+ <string name="power_remaining_fast_charging_duration_only_v2">Full by <xliff:g id="time">%1$s</xliff:g></string>
+
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_unknown">Unknown</string>
<!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging from an unknown source. -->
@@ -1171,6 +1181,11 @@
<!-- [CHAR_LIMIT=None] Battery Info screen. Value for a status item. A state which device charging on hold -->
<string name="battery_info_status_charging_on_hold">Charging on hold</string>
+ <!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging isn't fast. -->
+ <string name="battery_info_status_charging_v2">Charging</string>
+ <!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging speed is fast. -->
+ <string name="battery_info_status_charging_fast_v2">Fast charging</string>
+
<!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] -->
<string name="disabled_by_admin_summary_text">Controlled by admin</string>
@@ -1658,6 +1673,8 @@
<string name="accessibility_phone_two_bars">Phone two bars.</string>
<!-- Content description of the phone signal when it is three bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_phone_three_bars">Phone three bars.</string>
+ <!-- Content description of the phone signal when it is four bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_phone_four_bars">Phone four bars.</string>
<!-- Content description of the phone signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_phone_signal_full">Phone signal full.</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
index ce466df..9073d28 100644
--- a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
@@ -33,6 +33,59 @@
R.string.accessibility_phone_signal_full
};
+ /**
+ * @param level int in range [0-4] that describes the signal level
+ * @return the appropriate content description for that signal strength, or 0 if the param is
+ * invalid
+ */
+ public static int getDescriptionForLevel(int level) {
+ if (level > 4 || level < 0) {
+ return 0;
+ }
+
+ return PHONE_SIGNAL_STRENGTH[level];
+ }
+
+ public static final int[] PHONE_SIGNAL_STRENGTH_INFLATED = {
+ PHONE_SIGNAL_STRENGTH_NONE,
+ R.string.accessibility_phone_one_bar,
+ R.string.accessibility_phone_two_bars,
+ R.string.accessibility_phone_three_bars,
+ R.string.accessibility_phone_four_bars,
+ R.string.accessibility_phone_signal_full
+ };
+
+ /**
+ * @param level int in range [0-5] that describes the inflated signal level
+ * @return the appropriate content description for that signal strength, or 0 if the param is
+ * invalid
+ */
+ public static int getDescriptionForInflatedLevel(int level) {
+ if (level > 5 || level < 0) {
+ return 0;
+ }
+
+ return PHONE_SIGNAL_STRENGTH_INFLATED[level];
+ }
+
+ /**
+ * @param level int in range [0-5] that describes the inflated signal level
+ * @param numberOfLevels one of (4, 5) that describes the default number of levels, or the
+ * inflated number of levels. The level param should be relative to the
+ * number of levels. This won't do any inflation.
+ * @return the appropriate content description for that signal strength, or 0 if the param is
+ * invalid
+ */
+ public static int getDescriptionForLevel(int level, int numberOfLevels) {
+ if (numberOfLevels == 5) {
+ return getDescriptionForLevel(level);
+ } else if (numberOfLevels == 6) {
+ return getDescriptionForInflatedLevel(level);
+ } else {
+ return 0;
+ }
+ }
+
public static final int[] DATA_CONNECTION_STRENGTH = {
R.string.accessibility_no_data,
R.string.accessibility_data_one_bar,
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index e95a506..563f02d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -65,6 +65,7 @@
import com.android.launcher3.util.UserIconInfo;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.settingslib.fuelgauge.BatteryUtils;
import com.android.settingslib.utils.BuildCompatUtils;
import java.util.List;
@@ -246,25 +247,23 @@
} else {
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
if (compactStatus) {
- statusString = res.getString(R.string.battery_info_status_charging);
+ statusString = getRegularChargingStatusString(res);
} else if (batteryStatus.isPluggedInWired()) {
switch (batteryStatus.getChargingSpeed(context)) {
case BatteryStatus.CHARGING_FAST:
- statusString =
- res.getString(R.string.battery_info_status_charging_fast);
+ statusString = getFastChargingStatusString(res);
break;
case BatteryStatus.CHARGING_SLOWLY:
- statusString =
- res.getString(R.string.battery_info_status_charging_slow);
+ statusString = getSlowChargingStatusString(res);
break;
default:
- statusString = res.getString(R.string.battery_info_status_charging);
+ statusString = getRegularChargingStatusString(res);
break;
}
} else if (batteryStatus.isPluggedInDock()) {
- statusString = res.getString(R.string.battery_info_status_charging_dock);
+ statusString = getDockChargingStatusString(res);
} else {
- statusString = res.getString(R.string.battery_info_status_charging_wireless);
+ statusString = getWirelessChargingStatusString(res);
}
} else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
statusString = res.getString(R.string.battery_info_status_discharging);
@@ -276,6 +275,41 @@
return statusString;
}
+ private static String getFastChargingStatusString(Resources res) {
+ return res.getString(
+ BatteryUtils.isChargingStringV2Enabled()
+ ? R.string.battery_info_status_charging_fast_v2
+ : R.string.battery_info_status_charging_fast);
+ }
+
+ private static String getSlowChargingStatusString(Resources res) {
+ return res.getString(
+ BatteryUtils.isChargingStringV2Enabled()
+ ? R.string.battery_info_status_charging_v2
+ : R.string.battery_info_status_charging_slow);
+ }
+
+ private static String getRegularChargingStatusString(Resources res) {
+ return res.getString(
+ BatteryUtils.isChargingStringV2Enabled()
+ ? R.string.battery_info_status_charging_v2
+ : R.string.battery_info_status_charging);
+ }
+
+ private static String getWirelessChargingStatusString(Resources res) {
+ return res.getString(
+ BatteryUtils.isChargingStringV2Enabled()
+ ? R.string.battery_info_status_charging_v2
+ : R.string.battery_info_status_charging_wireless);
+ }
+
+ private static String getDockChargingStatusString(Resources res) {
+ return res.getString(
+ BatteryUtils.isChargingStringV2Enabled()
+ ? R.string.battery_info_status_charging_v2
+ : R.string.battery_info_status_charging_dock);
+ }
+
public static ColorStateList getColorAccent(Context context) {
return getColorAttr(context, android.R.attr.colorAccent);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 56118da..06c41cb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -1993,6 +1993,40 @@
};
/**
+ * Displays a combined list with "downloaded" and "visible in launcher" apps which belong to a
+ * user which is either not in quiet mode or allows showing apps even when in quiet mode.
+ */
+ public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET = new AppFilter() {
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(@NonNull AppEntry entry) {
+ if (entry.hideInQuietMode) {
+ return false;
+ }
+ if (AppUtils.isInstant(entry.info)) {
+ return false;
+ } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
+ return true;
+ } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
+ return true;
+ } else if (entry.hasLauncherEntry) {
+ return true;
+ } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM) && entry.isHomeApp) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void refreshAppEntryOnRebuild(@NonNull AppEntry appEntry, boolean hideInQuietMode) {
+ appEntry.hideInQuietMode = hideInQuietMode;
+ }
+ };
+
+ /**
* Displays a combined list with "downloaded" and "visible in launcher" apps only.
*/
public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT = new AppFilter() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 0996d52..e926b16 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -57,6 +57,7 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final LocalBluetoothAdapter mLocalAdapter;
+ private final LocalBluetoothManager mBtManager;
private final CachedBluetoothDeviceManager mDeviceManager;
private final IntentFilter mAdapterIntentFilter, mProfileIntentFilter;
private final Map<String, Handler> mHandlerMap;
@@ -80,10 +81,15 @@
* userHandle passed in is {@code null}, we register event receiver for the
* {@code context.getUser()} handle.
*/
- BluetoothEventManager(LocalBluetoothAdapter adapter,
- CachedBluetoothDeviceManager deviceManager, Context context,
- android.os.Handler handler, @Nullable UserHandle userHandle) {
+ BluetoothEventManager(
+ LocalBluetoothAdapter adapter,
+ LocalBluetoothManager btManager,
+ CachedBluetoothDeviceManager deviceManager,
+ Context context,
+ android.os.Handler handler,
+ @Nullable UserHandle userHandle) {
mLocalAdapter = adapter;
+ mBtManager = btManager;
mDeviceManager = deviceManager;
mAdapterIntentFilter = new IntentFilter();
mProfileIntentFilter = new IntentFilter();
@@ -210,11 +216,27 @@
}
}
- void dispatchProfileConnectionStateChanged(@NonNull CachedBluetoothDevice device, int state,
- int bluetoothProfile) {
+ void dispatchProfileConnectionStateChanged(
+ @NonNull CachedBluetoothDevice device, int state, int bluetoothProfile) {
for (BluetoothCallback callback : mCallbacks) {
callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
}
+
+ // Trigger updateFallbackActiveDeviceIfNeeded when ASSISTANT profile disconnected when
+ // audio sharing is enabled.
+ if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
+ && state == BluetoothAdapter.STATE_DISCONNECTED
+ && BluetoothUtils.isAudioSharingEnabled()) {
+ LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
+ if (profileManager != null
+ && profileManager.getLeAudioBroadcastProfile() != null
+ && profileManager.getLeAudioBroadcastProfile().isProfileReady()
+ && profileManager.getLeAudioBroadcastAssistantProfile() != null
+ && profileManager.getLeAudioBroadcastAssistantProfile().isProfileReady()) {
+ Log.d(TAG, "updateFallbackActiveDeviceIfNeeded, ASSISTANT profile disconnected");
+ profileManager.getLeAudioBroadcastProfile().updateFallbackActiveDeviceIfNeeded();
+ }
+ }
}
private void dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
@@ -536,7 +558,6 @@
default:
Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
return;
-
}
dispatchAclStateChanged(activeDevice, state);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 4777b0d..04516eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -16,6 +16,8 @@
package com.android.settingslib.bluetooth;
+import static com.android.settingslib.flags.Flags.enableSetPreferredTransportForLeAudioDevice;
+
import android.annotation.CallbackExecutor;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
@@ -288,6 +290,10 @@
mLocalNapRoleConnected = true;
}
}
+ if (enableSetPreferredTransportForLeAudioDevice()
+ && profile instanceof HidProfile) {
+ updatePreferredTransport();
+ }
} else if (profile instanceof MapProfile
&& newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
profile.setEnabled(mDevice, false);
@@ -300,12 +306,34 @@
mLocalNapRoleConnected = false;
}
+ if (enableSetPreferredTransportForLeAudioDevice()
+ && profile instanceof LeAudioProfile) {
+ updatePreferredTransport();
+ }
+
HearingAidStatsLogUtils.updateHistoryIfNeeded(mContext, this, profile, newProfileState);
}
fetchActiveDevices();
}
+ private void updatePreferredTransport() {
+ if (mProfiles.stream().noneMatch(p -> p instanceof LeAudioProfile)
+ || mProfiles.stream().noneMatch(p -> p instanceof HidProfile)) {
+ return;
+ }
+ // Both LeAudioProfile and HidProfile are connectable.
+ if (!mProfileManager
+ .getHidProfile()
+ .setPreferredTransport(
+ mDevice,
+ mProfileManager.getLeAudioProfile().isEnabled(mDevice)
+ ? BluetoothDevice.TRANSPORT_LE
+ : BluetoothDevice.TRANSPORT_BREDR)) {
+ Log.w(TAG, "Fail to set preferred transport");
+ }
+ }
+
@VisibleForTesting
void setProfileConnectedStatus(int profileId, boolean isFailed) {
switch (profileId) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 5b91ac9..b849d44 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -27,6 +27,8 @@
import android.content.Context;
import android.util.Log;
+import androidx.annotation.NonNull;
+
import com.android.settingslib.R;
import java.util.List;
@@ -187,6 +189,14 @@
}
}
+ /** Set preferred transport for the device */
+ public boolean setPreferredTransport(@NonNull BluetoothDevice device, int transport) {
+ if (mService != null) {
+ mService.setPreferredTransport(device, transport);
+ }
+ return false;
+ }
+
protected void finalize() {
Log.d(TAG, "finalize()");
if (mService != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 6c12cb7..9df23aa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -42,6 +42,7 @@
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
+import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -336,7 +337,6 @@
+ ", sourceId = "
+ sourceId);
}
- updateFallbackActiveDeviceIfNeeded();
}
@Override
@@ -468,6 +468,15 @@
mServiceBroadcast.startBroadcast(settings);
}
+ /** Checks if the broadcast is playing. */
+ public boolean isPlaying(int broadcastId) {
+ if (mServiceBroadcast == null) {
+ Log.d(TAG, "check isPlaying failed, the BluetoothLeBroadcast is null.");
+ return false;
+ }
+ return mServiceBroadcast.isPlaying(broadcastId);
+ }
+
private BluetoothLeBroadcastSettings buildBroadcastSettings(
boolean isPublic,
@Nullable String broadcastName,
@@ -1025,6 +1034,16 @@
/** Update fallback active device if needed. */
public void updateFallbackActiveDeviceIfNeeded() {
+ if (mServiceBroadcast == null) {
+ Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded due to broadcast profile is null");
+ return;
+ }
+ List<BluetoothLeBroadcastMetadata> sources = mServiceBroadcast.getAllBroadcastMetadata();
+ if (sources.stream()
+ .noneMatch(source -> mServiceBroadcast.isPlaying(source.getBroadcastId()))) {
+ Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded due to no broadcast ongoing");
+ return;
+ }
if (mServiceBroadcastAssistant == null) {
Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded due to assistant profile is null");
return;
@@ -1117,10 +1136,19 @@
Log.d(TAG, "Skip notifyBroadcastStateChange, not triggered by Settings.");
return;
}
+ if (isWorkProfile(mContext)) {
+ Log.d(TAG, "Skip notifyBroadcastStateChange, not triggered for work profile.");
+ return;
+ }
Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_STATE_CHANGE);
intent.putExtra(EXTRA_LE_AUDIO_SHARING_STATE, state);
intent.setPackage(mContext.getPackageName());
Log.e(TAG, "notifyBroadcastStateChange for state = " + state);
mContext.sendBroadcast(intent);
}
+
+ private boolean isWorkProfile(Context context) {
+ UserManager userManager = context.getSystemService(UserManager.class);
+ return userManager != null && userManager.isManagedProfile();
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
index 34c60a1..9faebe2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
@@ -381,6 +381,14 @@
});
}
+ /** Gets devices with matched connection states. */
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[] states) {
+ if (mService == null) {
+ return new ArrayList<BluetoothDevice>(0);
+ }
+ return mService.getDevicesMatchingConnectionStates(states);
+ }
+
public boolean isEnabled(BluetoothDevice device) {
if (mService == null || device == null) {
return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
index 53c6075..c4300d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
@@ -21,11 +21,11 @@
import android.os.UserHandle;
import android.util.Log;
-import java.lang.ref.WeakReference;
-
import androidx.annotation.Nullable;
import androidx.annotation.RequiresPermission;
+import java.lang.ref.WeakReference;
+
/**
* LocalBluetoothManager provides a simplified interface on top of a subset of
* the Bluetooth API. Note that {@link #getInstance} will return null
@@ -111,10 +111,17 @@
mContext = context.getApplicationContext();
mLocalAdapter = adapter;
mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, this);
- mEventManager = new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mContext,
- handler, userHandle);
- mProfileManager = new LocalBluetoothProfileManager(mContext,
- mLocalAdapter, mCachedDeviceManager, mEventManager);
+ mEventManager =
+ new BluetoothEventManager(
+ mLocalAdapter,
+ this,
+ mCachedDeviceManager,
+ mContext,
+ handler,
+ userHandle);
+ mProfileManager =
+ new LocalBluetoothProfileManager(
+ mContext, mLocalAdapter, mCachedDeviceManager, mEventManager);
mProfileManager.updateLocalProfiles();
mEventManager.readPairedDevices();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 79e4c37..4055986 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -572,8 +572,7 @@
return mSapProfile;
}
- @VisibleForTesting
- HidProfile getHidProfile() {
+ public HidProfile getHidProfile() {
return mHidProfile;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
index 92db508..327e470 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
@@ -21,11 +21,14 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.provider.Settings;
+import android.os.SystemProperties;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.ArraySet;
import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.VisibleForTesting;
+
import java.util.List;
public final class BatteryUtils {
@@ -33,6 +36,9 @@
/** The key to get the time to full from Settings.Global */
public static final String GLOBAL_TIME_TO_FULL_MILLIS = "time_to_full_millis";
+ /** The system property key to check whether the charging string v2 is enabled or not. */
+ public static final String PROPERTY_CHARGING_STRING_V2_KEY = "charging_string.apply_v2";
+
/** Gets the latest sticky battery intent from the Android system. */
public static Intent getBatteryIntent(Context context) {
return context.registerReceiver(
@@ -75,4 +81,25 @@
final UserManager userManager = context.getSystemService(UserManager.class);
return userManager.isManagedProfile() && !userManager.isSystemUser();
}
+
+ private static Boolean sChargingStringV2Enabled = null;
+
+ /** Returns {@code true} if the charging string v2 is enabled. */
+ public static boolean isChargingStringV2Enabled() {
+ if (sChargingStringV2Enabled == null) {
+ sChargingStringV2Enabled =
+ SystemProperties.getBoolean(PROPERTY_CHARGING_STRING_V2_KEY, false);
+ }
+ return sChargingStringV2Enabled;
+ }
+
+
+ /** Used to override the system property to enable or reset for charging string V2. */
+ @VisibleForTesting
+ public static void setChargingStringV2Enabled(Boolean enabled) {
+ SystemProperties.set(
+ BatteryUtils.PROPERTY_CHARGING_STRING_V2_KEY,
+ enabled == null ? "" : String.valueOf(enabled));
+ BatteryUtils.sChargingStringV2Enabled = enabled;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
index 8fd4e91..59e9c75 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
@@ -26,6 +26,7 @@
import android.os.IDeviceIdleController;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.telecom.DefaultDialerManager;
import android.text.TextUtils;
@@ -121,6 +122,14 @@
return true;
}
+ if (android.app.admin.flags.Flags.disallowUserControlBgUsageFix()) {
+ // App is subject to DevicePolicyManager.setUserControlDisabledPackages() policy.
+ final int userId = UserHandle.getUserId(uid);
+ if (mAppContext.getPackageManager().isPackageStateProtected(pkg, userId)) {
+ return true;
+ }
+ }
+
return false;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
index 6fb0179..e91c0bc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
@@ -168,17 +168,16 @@
/**
* Gets a list of preferences that other apps have injected.
*
- * @param profileId Identifier of the user/profile to obtain the injected settings for or
- * UserHandle.USER_CURRENT for all profiles associated with current user.
+ * @param profiles UserHandles of the users/profiles for which to obtain the injected settings.
*/
public Map<Integer, List<Preference>> getInjectedSettings(Context prefContext,
- final int profileId) {
+ final Set<UserHandle> profiles) {
final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- final List<UserHandle> profiles = um.getUserProfiles();
+ final List<UserHandle> allProfilesForUser = um.getUserProfiles();
final ArrayMap<Integer, List<Preference>> result = new ArrayMap<>();
mSettings.clear();
- for (UserHandle userHandle : profiles) {
- if (profileId == UserHandle.USER_CURRENT || profileId == userHandle.getIdentifier()) {
+ for (UserHandle userHandle : allProfilesForUser) {
+ if (profiles.contains(userHandle)) {
final List<Preference> prefs = new ArrayList<>();
Iterable<InjectedSetting> settings = getSettings(userHandle);
for (InjectedSetting setting : settings) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 9b1e4b7..eae58ad 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -52,6 +52,7 @@
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
import android.os.Build;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -131,6 +132,7 @@
protected final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
@NonNull protected final Context mContext;
@NonNull protected final String mPackageName;
+ @NonNull protected final UserHandle mUserHandle;
private final Collection<MediaDeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
private MediaDevice mCurrentConnectedDevice;
private final LocalBluetoothManager mBluetoothManager;
@@ -140,16 +142,28 @@
/* package */ InfoMediaManager(
@NonNull Context context,
@NonNull String packageName,
+ @NonNull UserHandle userHandle,
@NonNull LocalBluetoothManager localBluetoothManager) {
mContext = context;
mBluetoothManager = localBluetoothManager;
mPackageName = packageName;
+ mUserHandle = userHandle;
}
- /** Creates an instance of InfoMediaManager. */
+ /**
+ * Creates an instance of InfoMediaManager.
+ *
+ * @param context The {@link Context}.
+ * @param packageName The package name of the app for which to control routing, or null if the
+ * caller is interested in system-level routing only (for example, headsets, built-in
+ * speakers, as opposed to app-specific routing (for example, casting to another device).
+ * @param userHandle The {@link UserHandle} of the user on which the app to control is running,
+ * or null if the caller does not need app-specific routing (see {@code packageName}).
+ */
public static InfoMediaManager createInstance(
Context context,
@Nullable String packageName,
+ @Nullable UserHandle userHandle,
LocalBluetoothManager localBluetoothManager) {
// The caller is only interested in system routes (headsets, built-in speakers, etc), and is
@@ -159,25 +173,28 @@
packageName = context.getPackageName();
}
+ if (userHandle == null) {
+ userHandle = android.os.Process.myUserHandle();
+ }
+
if (Flags.useMediaRouter2ForInfoMediaManager()) {
try {
- return new RouterInfoMediaManager(context, packageName, localBluetoothManager);
+ return new RouterInfoMediaManager(
+ context, packageName, userHandle, localBluetoothManager);
} catch (PackageNotAvailableException ex) {
// TODO: b/293578081 - Propagate this exception to callers for proper handling.
Log.w(TAG, "Returning a no-op InfoMediaManager for package " + packageName);
- return new NoOpInfoMediaManager(context, packageName, localBluetoothManager);
+ return new NoOpInfoMediaManager(
+ context, packageName, userHandle, localBluetoothManager);
}
} else {
- return new ManagerInfoMediaManager(context, packageName, localBluetoothManager);
+ return new ManagerInfoMediaManager(
+ context, packageName, userHandle, localBluetoothManager);
}
}
public void startScan() {
- mMediaDevices.clear();
- registerRouter();
startScanOnRouter();
- updateRouteListingPreference();
- refreshDevices();
}
private void updateRouteListingPreference() {
@@ -191,7 +208,6 @@
public final void stopScan() {
stopScanOnRouter();
- unregisterRouter();
}
protected abstract void stopScanOnRouter();
@@ -278,14 +294,37 @@
return null;
}
- protected final void registerCallback(MediaDeviceCallback callback) {
+ /**
+ * Registers the specified {@code callback} to receive state updates about routing information.
+ *
+ * <p>As long as there is a registered {@link MediaDeviceCallback}, {@link InfoMediaManager}
+ * will receive state updates from the platform.
+ *
+ * <p>Call {@link #unregisterCallback(MediaDeviceCallback)} once you no longer need platform
+ * updates.
+ */
+ public final void registerCallback(@NonNull MediaDeviceCallback callback) {
+ boolean wasEmpty = mCallbacks.isEmpty();
if (!mCallbacks.contains(callback)) {
mCallbacks.add(callback);
+ if (wasEmpty) {
+ mMediaDevices.clear();
+ registerRouter();
+ updateRouteListingPreference();
+ refreshDevices();
+ }
}
}
- protected final void unregisterCallback(MediaDeviceCallback callback) {
- mCallbacks.remove(callback);
+ /**
+ * Unregisters the specified {@code callback}.
+ *
+ * @see #registerCallback(MediaDeviceCallback)
+ */
+ public final void unregisterCallback(@NonNull MediaDeviceCallback callback) {
+ if (mCallbacks.remove(callback) && mCallbacks.isEmpty()) {
+ unregisterRouter();
+ }
}
private void dispatchDeviceListAdded(@NonNull List<MediaDevice> devices) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 63056b6..473c627 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -17,7 +17,6 @@
import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
-import android.app.Notification;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
@@ -106,14 +105,23 @@
* Register to start receiving callbacks for MediaDevice events.
*/
public void registerCallback(DeviceCallback callback) {
- mCallbacks.add(callback);
+ boolean wasEmpty = mCallbacks.isEmpty();
+ if (!mCallbacks.contains(callback)) {
+ mCallbacks.add(callback);
+ if (wasEmpty) {
+ mInfoMediaManager.registerCallback(mMediaDeviceCallback);
+ }
+ }
}
/**
* Unregister to stop receiving callbacks for MediaDevice events
*/
public void unregisterCallback(DeviceCallback callback) {
- mCallbacks.remove(callback);
+ if (mCallbacks.remove(callback) && mCallbacks.isEmpty()) {
+ mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
+ unRegisterDeviceAttributeChangeCallback();
+ }
}
/**
@@ -125,7 +133,7 @@
*
* It will use {@link BluetoothAdapter#getDefaultAdapter()] for setting the bluetooth adapter.
*/
- public LocalMediaManager(Context context, String packageName, Notification notification) {
+ public LocalMediaManager(Context context, String packageName) {
mContext = context;
mPackageName = packageName;
mLocalBluetoothManager =
@@ -138,7 +146,10 @@
}
mInfoMediaManager =
- InfoMediaManager.createInstance(context, packageName, mLocalBluetoothManager);
+ // TODO: b/321969740 - Take the userHandle as a parameter and pass it through. The
+ // package name is not sufficient to unambiguously identify an app.
+ InfoMediaManager.createInstance(
+ context, packageName, /* userHandle */ null, mLocalBluetoothManager);
}
/**
@@ -225,10 +236,6 @@
* Start scan connected MediaDevice
*/
public void startScan() {
- synchronized (mMediaDevicesLock) {
- mMediaDevices.clear();
- }
- mInfoMediaManager.registerCallback(mMediaDeviceCallback);
mInfoMediaManager.startScan();
}
@@ -278,9 +285,7 @@
* Stop scan MediaDevice
*/
public void stopScan() {
- mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
mInfoMediaManager.stopScan();
- unRegisterDeviceAttributeChangeCallback();
}
/**
@@ -551,7 +556,6 @@
private MediaDevice getMutingExpectedDevice() {
if (mBluetoothAdapter == null
|| mAudioManager.getMutingExpectedDevice() == null) {
- Log.w(TAG, "BluetoothAdapter is null or muting expected device not exist");
return null;
}
final List<BluetoothDevice> bluetoothDevices =
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index 23063da..d621751 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -21,6 +21,7 @@
import android.media.MediaRouter2Manager;
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -53,8 +54,9 @@
/* package */ ManagerInfoMediaManager(
Context context,
@NonNull String packageName,
+ @NonNull UserHandle userHandle,
LocalBluetoothManager localBluetoothManager) {
- super(context, packageName, localBluetoothManager);
+ super(context, packageName, userHandle, localBluetoothManager);
mRouterManager = MediaRouter2Manager.getInstance(context);
}
@@ -87,8 +89,7 @@
@Override
protected void transferToRoute(@NonNull MediaRoute2Info route) {
- // TODO: b/279555229 - provide real user handle of a caller.
- mRouterManager.transfer(mPackageName, route, android.os.Process.myUserHandle());
+ mRouterManager.transfer(mPackageName, route, mUserHandle);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
index cf11c6d..d2b018c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
@@ -20,6 +20,7 @@
import android.media.MediaRoute2Info;
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
+import android.os.UserHandle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -58,8 +59,9 @@
NoOpInfoMediaManager(
Context context,
@NonNull String packageName,
+ @NonNull UserHandle userHandle,
LocalBluetoothManager localBluetoothManager) {
- super(context, packageName, localBluetoothManager);
+ super(context, packageName, userHandle, localBluetoothManager);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
index 0dceeba..045c60d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
@@ -25,7 +25,7 @@
import android.media.RouteDiscoveryPreference;
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
-import android.os.Process;
+import android.os.UserHandle;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@@ -70,15 +70,16 @@
/* package */ RouterInfoMediaManager(
Context context,
@NonNull String packageName,
+ @NonNull UserHandle userHandle,
LocalBluetoothManager localBluetoothManager)
throws PackageNotAvailableException {
- super(context, packageName, localBluetoothManager);
+ super(context, packageName, userHandle, localBluetoothManager);
MediaRouter2 router = null;
if (Flags.enableCrossUserRoutingInMediaRouter2()) {
try {
- router = MediaRouter2.getInstance(context, packageName, Process.myUserHandle());
+ router = MediaRouter2.getInstance(context, packageName, userHandle);
} catch (IllegalArgumentException ex) {
// Do nothing
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
index b015b2b..46f2290 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
@@ -184,7 +184,7 @@
dialogHelper
.setTitle(R.string.user_info_settings_title)
.addCustomView(content)
- .setPositiveButton(android.R.string.ok, view -> {
+ .setPositiveButton(R.string.okay, view -> {
Drawable newUserIcon = mEditUserPhotoController != null
? mEditUserPhotoController.getNewUserPhotoDrawable()
: null;
@@ -201,7 +201,7 @@
}
dialogHelper.getDialog().dismiss();
})
- .setBackButton(android.R.string.cancel, view -> {
+ .setBackButton(R.string.cancel, view -> {
clear();
if (cancelCallback != null) {
cancelCallback.run();
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
index 2272654..5ed5999 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -33,7 +33,7 @@
import java.util.Locale;
import java.util.concurrent.TimeUnit;
-/** Utility class for keeping power related strings consistent**/
+/** Utility class for keeping power related strings consistent. **/
public class PowerUtil {
private static final long SEVEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(7);
@@ -221,4 +221,19 @@
return time - remainder + multiple;
}
}
+
+ /** Gets the rounded target time string in a short format. */
+ public static String getTargetTimeShortString(
+ Context context, long targetTimeOffsetMs, long currentTimeMs) {
+ final long roundedTimeOfDayMs =
+ roundTimeToNearestThreshold(
+ currentTimeMs + targetTimeOffsetMs, FIFTEEN_MINUTES_MILLIS);
+
+ // convert the time to a properly formatted string.
+ String skeleton = android.text.format.DateFormat.getTimeFormatString(context);
+ DateFormat fmt = DateFormat.getInstanceForSkeleton(skeleton);
+ Date date = Date.from(Instant.ofEpochMilli(roundedTimeOfDayMs));
+ return fmt.format(date);
+ }
}
+
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
index 724dd51..869fb7f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
@@ -17,6 +17,7 @@
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
+import com.android.settingslib.media.flags.Flags
import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
import com.android.settingslib.volume.shared.model.AudioManagerEvent
import kotlinx.coroutines.CoroutineScope
@@ -69,10 +70,14 @@
}
}
localMediaManager.registerCallback(callback)
- localMediaManager.startScan()
+ if (!Flags.removeUnnecessaryRouteScanning()) {
+ localMediaManager.startScan()
+ }
awaitClose {
- localMediaManager.stopScan()
+ if (!Flags.removeUnnecessaryRouteScanning()) {
+ localMediaManager.stopScan()
+ }
localMediaManager.unregisterCallback(callback)
}
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
index b974888..fef0561 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
@@ -240,6 +240,56 @@
}
@Test
+ public void testDownloadAndLauncherNotInQuietAcceptsCorrectApps() {
+ mEntry.isHomeApp = false;
+ mEntry.hasLauncherEntry = false;
+
+ // should include updated system apps
+ when(mEntry.info.isInstantApp()).thenReturn(false);
+ mEntry.info.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isTrue();
+
+ // should not include system apps other than the home app
+ mEntry.info.flags = ApplicationInfo.FLAG_SYSTEM;
+ mEntry.isHomeApp = false;
+ mEntry.hasLauncherEntry = false;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isFalse();
+
+ // should include the home app
+ mEntry.isHomeApp = true;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isTrue();
+
+ // should include any System app with a launcher entry
+ mEntry.isHomeApp = false;
+ mEntry.hasLauncherEntry = true;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isTrue();
+
+ // should not include updated system apps when in quiet mode
+ when(mEntry.info.isInstantApp()).thenReturn(false);
+ mEntry.info.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ mEntry.hideInQuietMode = true;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isFalse();
+
+ // should not include the home app when in quiet mode
+ mEntry.isHomeApp = true;
+ mEntry.hideInQuietMode = true;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isFalse();
+
+ // should not include any System app with a launcher entry when in quiet mode
+ mEntry.isHomeApp = false;
+ mEntry.hasLauncherEntry = true;
+ mEntry.hideInQuietMode = true;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isFalse();
+ }
+
+ @Test
public void testOtherAppsRejectsLegacyGame() {
mEntry.info.flags = ApplicationInfo.FLAG_IS_GAME;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
index 50f5b9d..69f6305 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
@@ -20,6 +20,8 @@
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -37,13 +39,11 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
import java.util.concurrent.CountDownLatch;
/**
- * Test that verifies that BluetoothEventManager can receive broadcasts for non-current
- * users for all bluetooth events.
+ * Test that verifies that BluetoothEventManager can receive broadcasts for non-current users for
+ * all bluetooth events.
*
* <p>Creation and deletion of users takes a long time, so marking this as a LargeTest.
*/
@@ -64,9 +64,14 @@
mContext = InstrumentationRegistry.getTargetContext();
mUserManager = UserManager.get(mContext);
- mBluetoothEventManager = new BluetoothEventManager(
- mock(LocalBluetoothAdapter.class), mock(CachedBluetoothDeviceManager.class),
- mContext, /* handler= */ null, UserHandle.ALL);
+ mBluetoothEventManager =
+ new BluetoothEventManager(
+ mock(LocalBluetoothAdapter.class),
+ mock(LocalBluetoothManager.class),
+ mock(CachedBluetoothDeviceManager.class),
+ mContext,
+ /* handler= */ null,
+ UserHandle.ALL);
// Create and start another user in the background.
mOtherUser = mUserManager.createUser("TestUser", /* flags= */ 0);
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/InfoMediaManagerIntegTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/InfoMediaManagerIntegTest.java
index f0185b9..3bd37a2 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/InfoMediaManagerIntegTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/InfoMediaManagerIntegTest.java
@@ -64,21 +64,23 @@
@RequiresFlagsEnabled(FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER)
public void createInstance_withMR2FlagOn_returnsRouterInfoMediaManager() {
InfoMediaManager manager =
- InfoMediaManager.createInstance(mContext, mContext.getPackageName(), null);
+ InfoMediaManager.createInstance(
+ mContext, mContext.getPackageName(), mContext.getUser(), null);
assertThat(manager).isInstanceOf(RouterInfoMediaManager.class);
}
@Test
@RequiresFlagsEnabled(FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER)
public void createInstance_withMR2FlagOn_withFakePackage_returnsNoOpInfoMediaManager() {
- InfoMediaManager manager = InfoMediaManager.createInstance(mContext, FAKE_PACKAGE, null);
+ InfoMediaManager manager =
+ InfoMediaManager.createInstance(mContext, FAKE_PACKAGE, null, null);
assertThat(manager).isInstanceOf(NoOpInfoMediaManager.class);
}
@Test
@RequiresFlagsEnabled(FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER)
public void createInstance_withMR2FlagOn_withNullPackage_returnsRouterInfoMediaManager() {
- InfoMediaManager manager = InfoMediaManager.createInstance(mContext, null, null);
+ InfoMediaManager manager = InfoMediaManager.createInstance(mContext, null, null, null);
assertThat(manager).isInstanceOf(RouterInfoMediaManager.class);
}
@@ -86,7 +88,8 @@
@RequiresFlagsDisabled(FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER)
public void createInstance_withMR2FlagOff_returnsManagerInfoMediaManager() {
InfoMediaManager manager =
- InfoMediaManager.createInstance(mContext, mContext.getPackageName(), null);
+ InfoMediaManager.createInstance(
+ mContext, mContext.getPackageName(), mContext.getUser(), null);
assertThat(manager).isInstanceOf(ManagerInfoMediaManager.class);
}
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/OWNERS b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/OWNERS
new file mode 100644
index 0000000..384fd9b
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/OWNERS
@@ -0,0 +1,3 @@
+#Android Media Better Together
+ivanbuper@google.com
+aquilescanta@google.com
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 48bbf4e..b1489be 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -22,6 +22,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -29,35 +30,47 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothStatusCodes;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.TelephonyManager;
import com.android.settingslib.R;
+import com.android.settingslib.flags.Flags;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
public class BluetoothEventManagerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String DEVICE_NAME = "test_device_name";
@Mock
private LocalBluetoothAdapter mLocalAdapter;
@Mock
+ private LocalBluetoothManager mBtManager;
+ @Mock
private CachedBluetoothDeviceManager mCachedDeviceManager;
@Mock
private BluetoothCallback mBluetoothCallback;
@@ -96,8 +109,15 @@
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- mBluetoothEventManager = new BluetoothEventManager(mLocalAdapter,
- mCachedDeviceManager, mContext, /* handler= */ null, /* userHandle= */ null);
+ mBluetoothEventManager =
+ new BluetoothEventManager(
+ mLocalAdapter,
+ mBtManager,
+ mCachedDeviceManager,
+ mContext,
+ /* handler= */ null,
+ /* userHandle= */ null);
+ when(mBtManager.getProfileManager()).thenReturn(mLocalProfileManager);
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
@@ -113,8 +133,13 @@
public void ifUserHandleIsNull_registerReceiverIsCalled() {
Context mockContext = mock(Context.class);
BluetoothEventManager eventManager =
- new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mockContext,
- /* handler= */ null, /* userHandle= */ null);
+ new BluetoothEventManager(
+ mLocalAdapter,
+ mBtManager,
+ mCachedDeviceManager,
+ mockContext,
+ /* handler= */ null,
+ /* userHandle= */ null);
verify(mockContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class),
eq(null), eq(null), eq(Context.RECEIVER_EXPORTED));
@@ -124,8 +149,13 @@
public void ifUserHandleSpecified_registerReceiverAsUserIsCalled() {
Context mockContext = mock(Context.class);
BluetoothEventManager eventManager =
- new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mockContext,
- /* handler= */ null, UserHandle.ALL);
+ new BluetoothEventManager(
+ mLocalAdapter,
+ mBtManager,
+ mCachedDeviceManager,
+ mockContext,
+ /* handler= */ null,
+ UserHandle.ALL);
verify(mockContext).registerReceiverAsUser(any(BroadcastReceiver.class), eq(UserHandle.ALL),
any(IntentFilter.class), eq(null), eq(null), eq(Context.RECEIVER_EXPORTED));
@@ -172,6 +202,160 @@
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
}
+ /**
+ * dispatchProfileConnectionStateChanged should not call {@link
+ * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when audio sharing flag is off.
+ */
+ @Test
+ public void dispatchProfileConnectionStateChanged_flagOff_noUpdateFallbackDevice() {
+ ShadowBluetoothAdapter shadowBluetoothAdapter =
+ Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
+ when(broadcast.isProfileReady()).thenReturn(true);
+ LocalBluetoothLeBroadcastAssistant assistant =
+ mock(LocalBluetoothLeBroadcastAssistant.class);
+ when(assistant.isProfileReady()).thenReturn(true);
+ LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
+ when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
+ when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
+ when(mBtManager.getProfileManager()).thenReturn(profileManager);
+ mBluetoothEventManager.dispatchProfileConnectionStateChanged(
+ mCachedBluetoothDevice,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+
+ verify(broadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
+ }
+
+ /**
+ * dispatchProfileConnectionStateChanged should not call {@link
+ * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when the device does not
+ * support audio sharing.
+ */
+ @Test
+ public void dispatchProfileConnectionStateChanged_notSupport_noUpdateFallbackDevice() {
+ ShadowBluetoothAdapter shadowBluetoothAdapter =
+ Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_NOT_SUPPORTED);
+ shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
+ when(broadcast.isProfileReady()).thenReturn(true);
+ LocalBluetoothLeBroadcastAssistant assistant =
+ mock(LocalBluetoothLeBroadcastAssistant.class);
+ when(assistant.isProfileReady()).thenReturn(true);
+ LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
+ when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
+ when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
+ when(mBtManager.getProfileManager()).thenReturn(profileManager);
+ mBluetoothEventManager.dispatchProfileConnectionStateChanged(
+ mCachedBluetoothDevice,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+
+ verify(broadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
+ }
+
+ /**
+ * dispatchProfileConnectionStateChanged should not call {@link
+ * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when audio sharing profile is
+ * not ready.
+ */
+ @Test
+ public void dispatchProfileConnectionStateChanged_profileNotReady_noUpdateFallbackDevice() {
+ ShadowBluetoothAdapter shadowBluetoothAdapter =
+ Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
+ when(broadcast.isProfileReady()).thenReturn(false);
+ LocalBluetoothLeBroadcastAssistant assistant =
+ mock(LocalBluetoothLeBroadcastAssistant.class);
+ when(assistant.isProfileReady()).thenReturn(true);
+ LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
+ when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
+ when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
+ when(mBtManager.getProfileManager()).thenReturn(profileManager);
+ mBluetoothEventManager.dispatchProfileConnectionStateChanged(
+ mCachedBluetoothDevice,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+
+ verify(broadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
+ }
+
+ /**
+ * dispatchProfileConnectionStateChanged should not call {@link
+ * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when triggered for profile
+ * other than LE_AUDIO_BROADCAST_ASSISTANT or state other than STATE_DISCONNECTED.
+ */
+ @Test
+ public void dispatchProfileConnectionStateChanged_notAssistantProfile_noUpdateFallbackDevice() {
+ ShadowBluetoothAdapter shadowBluetoothAdapter =
+ Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
+ when(broadcast.isProfileReady()).thenReturn(true);
+ LocalBluetoothLeBroadcastAssistant assistant =
+ mock(LocalBluetoothLeBroadcastAssistant.class);
+ when(assistant.isProfileReady()).thenReturn(true);
+ LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
+ when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
+ when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
+ when(mBtManager.getProfileManager()).thenReturn(profileManager);
+ mBluetoothEventManager.dispatchProfileConnectionStateChanged(
+ mCachedBluetoothDevice,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.LE_AUDIO);
+
+ verify(broadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
+ }
+
+ /**
+ * dispatchProfileConnectionStateChanged should call {@link
+ * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when assistant profile is
+ * disconnected and audio sharing is enabled.
+ */
+ @Test
+ public void dispatchProfileConnectionStateChanged_audioSharing_updateFallbackDevice() {
+ ShadowBluetoothAdapter shadowBluetoothAdapter =
+ Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
+ when(broadcast.isProfileReady()).thenReturn(true);
+ LocalBluetoothLeBroadcastAssistant assistant =
+ mock(LocalBluetoothLeBroadcastAssistant.class);
+ when(assistant.isProfileReady()).thenReturn(true);
+ LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
+ when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
+ when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
+ when(mBtManager.getProfileManager()).thenReturn(profileManager);
+ mBluetoothEventManager.dispatchProfileConnectionStateChanged(
+ mCachedBluetoothDevice,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+
+ verify(broadcast).updateFallbackActiveDeviceIfNeeded();
+ }
+
@Test
public void dispatchAclConnectionStateChanged_aclDisconnected_shouldDispatchCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 5996dbb..646e9eb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -15,6 +15,8 @@
*/
package com.android.settingslib.bluetooth;
+import static com.android.settingslib.flags.Flags.FLAG_ENABLE_SET_PREFERRED_TRANSPORT_FOR_LE_AUDIO_DEVICE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -86,6 +88,9 @@
private HapClientProfile mHapClientProfile;
@Mock
private LeAudioProfile mLeAudioProfile;
+
+ @Mock
+ private HidProfile mHidProfile;
@Mock
private BluetoothDevice mDevice;
@Mock
@@ -104,6 +109,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG);
+ mSetFlagsRule.enableFlags(FLAG_ENABLE_SET_PREFERRED_TRANSPORT_FOR_LE_AUDIO_DEVICE);
mContext = RuntimeEnvironment.application;
mAudioManager = mContext.getSystemService(AudioManager.class);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
@@ -118,6 +124,8 @@
when(mHearingAidProfile.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID);
when(mLeAudioProfile.isProfileReady()).thenReturn(true);
when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
+ when(mHidProfile.isProfileReady()).thenReturn(true);
+ when(mHidProfile.getProfileId()).thenReturn(BluetoothProfile.HID_HOST);
mCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mDevice));
mSubCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mSubDevice));
doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
@@ -1819,6 +1827,32 @@
assertThat(mCachedDevice.isConnectedHearingAidDevice()).isFalse();
}
+ @Test
+ public void leAudioHidDevice_leAudioEnabled_setPreferredTransportToLE() {
+
+ when(mProfileManager.getHidProfile()).thenReturn(mHidProfile);
+ when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+ when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(true);
+
+ updateProfileStatus(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
+
+ verify(mHidProfile).setPreferredTransport(mDevice, BluetoothDevice.TRANSPORT_LE);
+ }
+
+ @Test
+ public void leAudioHidDevice_leAudioDisabled_setPreferredTransportToBredr() {
+ when(mProfileManager.getHidProfile()).thenReturn(mHidProfile);
+ when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+ when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(false);
+
+ updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+
+ verify(mHidProfile).setPreferredTransport(mDevice, BluetoothDevice.TRANSPORT_BREDR);
+ }
+
private HearingAidInfo getLeftAshaHearingAidInfo() {
return new HearingAidInfo.Builder()
.setAshaDeviceSide(HearingAidProfile.DeviceSide.SIDE_LEFT)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index 4f8fa2f..cef0835 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -56,7 +56,8 @@
@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalBluetoothProfileManagerTest {
private final static long HISYNCID = 10;
-
+ @Mock
+ private LocalBluetoothManager mBtManager;
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
@@ -77,13 +78,21 @@
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mLocalBluetoothAdapter = LocalBluetoothAdapter.getInstance();
- mEventManager = spy(new BluetoothEventManager(mLocalBluetoothAdapter, mDeviceManager,
- mContext, /* handler= */ null, /* userHandle= */ null));
+ mEventManager =
+ spy(
+ new BluetoothEventManager(
+ mLocalBluetoothAdapter,
+ mBtManager,
+ mDeviceManager,
+ mContext,
+ /* handler= */ null,
+ /* userHandle= */ null));
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
when(mDeviceManager.findDevice(mDevice)).thenReturn(mCachedBluetoothDevice);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mDevice);
- mProfileManager = new LocalBluetoothProfileManager(mContext, mLocalBluetoothAdapter,
- mDeviceManager, mEventManager);
+ mProfileManager =
+ new LocalBluetoothProfileManager(
+ mContext, mLocalBluetoothAdapter, mDeviceManager, mEventManager);
}
/**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index d793867..69faddf 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -35,6 +35,7 @@
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;
@@ -144,7 +145,8 @@
doReturn(mMediaSessionManager).when(mContext).getSystemService(
Context.MEDIA_SESSION_SERVICE);
mInfoMediaManager =
- new ManagerInfoMediaManager(mContext, TEST_PACKAGE_NAME, mLocalBluetoothManager);
+ new ManagerInfoMediaManager(
+ mContext, TEST_PACKAGE_NAME, mContext.getUser(), mLocalBluetoothManager);
mShadowRouter2Manager = ShadowRouter2Manager.getShadow();
mInfoMediaManager.mRouterManager = MediaRouter2Manager.getInstance(mContext);
}
@@ -750,35 +752,31 @@
@Test
public void onTransferred_getAvailableRoutes_shouldAddMediaDevice() {
- final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
- final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
- routingSessionInfos.add(sessionInfo);
- final List<String> selectedRoutes = new ArrayList<>();
- selectedRoutes.add(TEST_ID);
- when(sessionInfo.getSelectedRoutes()).thenReturn(selectedRoutes);
- mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+ mInfoMediaManager.mRouterManager = mRouterManager;
+ // Since test is running in Robolectric, return a fake session to avoid NPE.
+ when(mRouterManager.getRoutingSessions(anyString()))
+ .thenReturn(List.of(TEST_SYSTEM_ROUTING_SESSION));
+ when(mRouterManager.getSelectedRoutes(any()))
+ .thenReturn(List.of(TEST_SELECTED_SYSTEM_ROUTE));
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
mInfoMediaManager.registerCallback(mCallback);
- when(info.getDeduplicationIds()).thenReturn(Set.of());
- when(info.getId()).thenReturn(TEST_ID);
- when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ MediaDevice mediaDevice = mInfoMediaManager.getCurrentConnectedDevice();
+ assertThat(mediaDevice).isNotNull();
+ assertThat(mediaDevice.getId()).isEqualTo(TEST_SYSTEM_ROUTE_ID);
- final List<MediaRoute2Info> routes = new ArrayList<>();
- routes.add(info);
- mShadowRouter2Manager.setTransferableRoutes(routes);
+ when(mRouterManager.getRoutingSessions(anyString()))
+ .thenReturn(List.of(TEST_SYSTEM_ROUTING_SESSION, TEST_REMOTE_ROUTING_SESSION));
+ when(mRouterManager.getSelectedRoutes(any())).thenReturn(List.of(TEST_REMOTE_ROUTE));
- final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
- assertThat(mediaDevice).isNull();
-
- mInfoMediaManager.mMediaRouterCallback.onTransferred(sessionInfo, sessionInfo);
+ mInfoMediaManager.mMediaRouterCallback.onTransferred(
+ TEST_SYSTEM_ROUTING_SESSION, TEST_REMOTE_ROUTING_SESSION);
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
- assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+ assertThat(infoDevice).isNotNull();
+ assertThat(infoDevice.getId()).isEqualTo(TEST_REMOTE_ROUTE.getId());
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
- verify(mCallback).onConnectedDeviceChanged(TEST_ID);
+ verify(mCallback).onConnectedDeviceChanged(TEST_REMOTE_ROUTE.getId());
}
@Test
@@ -794,7 +792,8 @@
mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(TEST_SYSTEM_ROUTING_SESSION);
- verify(mCallback).onDeviceListAdded(any());
+ // Expecting 1st call after registerCallback() and 2nd call after onSessionUpdated().
+ verify(mCallback, times(2)).onDeviceListAdded(any());
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 693b7d0..ddb5419 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -21,6 +21,8 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -58,6 +60,7 @@
import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@@ -117,6 +120,13 @@
mInfoMediaManager = mock(
InfoMediaManager.class,
withSettings().useConstructor(mContext, TEST_PACKAGE_NAME, mLocalBluetoothManager));
+ doReturn(
+ List.of(
+ new RoutingSessionInfo.Builder(TEST_SESSION_ID, TEST_PACKAGE_NAME)
+ .addSelectedRoute(TEST_DEVICE_ID_1)
+ .build()))
+ .when(mInfoMediaManager)
+ .getRoutingSessionsForPackage();
mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1));
mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
@@ -124,15 +134,16 @@
new LocalMediaManager(
mContext, mLocalBluetoothManager, mInfoMediaManager, TEST_PACKAGE_NAME);
mLocalMediaManager.mAudioManager = mAudioManager;
+ mLocalMediaManager.registerCallback(mCallback);
+ clearInvocations(mCallback);
}
@Test
- public void startScan_mediaDevicesListShouldBeClear() {
+ public void onDeviceListAdded_shouldClearDeviceList() {
final MediaDevice device = mock(MediaDevice.class);
mLocalMediaManager.mMediaDevices.add(device);
-
assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
- mLocalMediaManager.startScan();
+ mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(Collections.emptyList());
assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
}
@@ -147,7 +158,6 @@
when(device.getId()).thenReturn(TEST_DEVICE_ID_1);
when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID);
- mLocalMediaManager.registerCallback(mCallback);
assertThat(mLocalMediaManager.connectDevice(device)).isTrue();
verify(mInfoMediaManager).connectToDevice(device);
}
@@ -158,7 +168,6 @@
mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1;
- mLocalMediaManager.registerCallback(mCallback);
assertThat(mLocalMediaManager.connectDevice(mInfoMediaDevice2)).isTrue();
assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
@@ -171,7 +180,6 @@
mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1;
- mLocalMediaManager.registerCallback(mCallback);
assertThat(mLocalMediaManager.connectDevice(mInfoMediaDevice1)).isFalse();
assertThat(mInfoMediaDevice1.getState()).isNotEqualTo(LocalMediaManager.MediaDeviceState
@@ -189,7 +197,6 @@
when(cachedDevice.isConnected()).thenReturn(false);
when(cachedDevice.isBusy()).thenReturn(false);
- mLocalMediaManager.registerCallback(mCallback);
assertThat(mLocalMediaManager.connectDevice(device)).isTrue();
verify(cachedDevice).connect();
@@ -252,7 +259,6 @@
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
- mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
@@ -274,7 +280,6 @@
when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
- mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
@@ -292,7 +297,6 @@
mLocalMediaManager.mMediaDevices.add(device2);
assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
- mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback
.onDeviceListRemoved(mLocalMediaManager.mMediaDevices);
@@ -302,19 +306,21 @@
@Test
public void onDeviceListRemoved_phoneDeviceNotLastDeviceAfterRemoveDeviceList_removeList() {
- final List<MediaDevice> devices = new ArrayList<>();
final MediaDevice device1 = mock(MediaDevice.class);
final MediaDevice device2 = mock(MediaDevice.class);
final MediaDevice device3 = mock(MediaDevice.class);
- devices.add(device1);
- devices.add(device3);
+
+ mLocalMediaManager.mMediaDevices.clear();
mLocalMediaManager.mMediaDevices.add(device1);
mLocalMediaManager.mMediaDevices.add(device2);
mLocalMediaManager.mMediaDevices.add(device3);
assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
- mLocalMediaManager.registerCallback(mCallback);
- mLocalMediaManager.mMediaDeviceCallback.onDeviceListRemoved(devices);
+
+ final List<MediaDevice> devicesToRemove = new ArrayList<>();
+ devicesToRemove.add(device1);
+ devicesToRemove.add(device3);
+ mLocalMediaManager.mMediaDeviceCallback.onDeviceListRemoved(devicesToRemove);
assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
verify(mCallback).onDeviceListUpdate(any());
@@ -332,7 +338,6 @@
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
- mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2);
assertThat(mLocalMediaManager.getCurrentConnectedDevice()).isEqualTo(device2);
@@ -352,7 +357,6 @@
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
- mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1);
verify(mCallback, never()).onDeviceAttributesChanged();
@@ -366,7 +370,6 @@
assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
.STATE_DISCONNECTED);
- mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1);
assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
@@ -375,7 +378,6 @@
@Test
public void onConnectedDeviceChanged_nullConnectedDevice_noException() {
- mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2);
}
@@ -392,7 +394,6 @@
when(cachedDevice.isConnected()).thenReturn(false);
when(cachedDevice.isBusy()).thenReturn(false);
- mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.connectDevice(device);
mLocalMediaManager.mDeviceAttributeChangeCallback.onDeviceAttributesChanged();
@@ -410,7 +411,6 @@
.STATE_CONNECTING);
assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
.STATE_CONNECTED);
- mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(REASON_UNKNOWN_ERROR);
assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
@@ -421,8 +421,6 @@
@Test
public void onDeviceAttributesChanged_shouldBeCalled() {
- mLocalMediaManager.registerCallback(mCallback);
-
mLocalMediaManager.mDeviceAttributeChangeCallback.onDeviceAttributesChanged();
verify(mCallback).onDeviceAttributesChanged();
@@ -475,7 +473,6 @@
when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
assertThat(mLocalMediaManager.mMediaDevices).hasSize(0);
- mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
@@ -497,7 +494,6 @@
when(cachedDevice.isConnected()).thenReturn(false);
when(cachedDevice.isBusy()).thenReturn(false);
- mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.connectDevice(device);
verify(cachedDevice).connect();
@@ -512,8 +508,6 @@
@Test
public void onRequestFailed_shouldDispatchOnRequestFailed() {
- mLocalMediaManager.registerCallback(mCallback);
-
mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(1);
verify(mCallback).onRequestFailed(1);
@@ -532,7 +526,6 @@
mShadowBluetoothAdapter = null;
assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
- mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/NoOpInfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/NoOpInfoMediaManagerTest.java
index d630301..908f50d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/NoOpInfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/NoOpInfoMediaManagerTest.java
@@ -46,6 +46,7 @@
new NoOpInfoMediaManager(
mContext,
/* packageName */ "FAKE_PACKAGE_NAME",
+ mContext.getUser(),
/* localBluetoothManager */ null);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 2e7905f..cbc382b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -20,30 +20,24 @@
import static org.mockito.Mockito.spy;
+import android.app.AlarmManager;
import android.content.Context;
+import androidx.test.core.app.ApplicationProvider;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
import java.time.Duration;
+import java.time.Instant;
+import java.util.Locale;
import java.util.regex.Pattern;
@RunWith(RobolectricTestRunner.class)
public class PowerUtilTest {
- private static final String TEST_BATTERY_LEVEL_10 = "10%";
- private static final long TEN_SEC_MILLIS = Duration.ofSeconds(10).toMillis();
- private static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
- private static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
- private static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
- private static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
- private static final long TEN_HOURS_MILLIS = Duration.ofHours(10).toMillis();
- private static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
- private static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about";
- private static final String ENHANCED_SUFFIX = " based on your usage";
private static final String BATTERY_RUN_OUT_PREFIX = "Battery may run out by";
// matches a time (ex: '1:15 PM', '2 AM', '23:00')
private static final String TIME_OF_DAY_REGEX = " (\\d)+:?(\\d)* ((AM)*)|((PM)*)";
@@ -55,29 +49,31 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mContext = spy(RuntimeEnvironment.application);
+ mContext = spy(ApplicationProvider.getApplicationContext());
}
@Test
public void getBatteryTipStringFormatted_moreThanOneDay_usesCorrectString() {
- String info = PowerUtil.getBatteryTipStringFormatted(mContext,
- THREE_DAYS_MILLIS);
+ var threeDayMillis = Duration.ofDays(3).toMillis();
- assertThat(info).isEqualTo("More than 3 days left");
+ String batteryTipString = PowerUtil.getBatteryTipStringFormatted(mContext, threeDayMillis);
+
+ assertThat(batteryTipString).isEqualTo("More than 3 days left");
}
@Test
public void getBatteryTipStringFormatted_lessThanOneDay_usesCorrectString() {
- String info = PowerUtil.getBatteryTipStringFormatted(mContext,
- SEVENTEEN_MIN_MILLIS);
+ var drainTimeMs = Duration.ofMinutes(17).toMillis();
+
+ String batteryTipString = PowerUtil.getBatteryTipStringFormatted(mContext, drainTimeMs);
// ex: Battery may run out by 1:15 PM
- assertThat(info).containsMatch(Pattern.compile(
- BATTERY_RUN_OUT_PREFIX + TIME_OF_DAY_REGEX));
+ assertThat(batteryTipString)
+ .containsMatch(Pattern.compile(BATTERY_RUN_OUT_PREFIX + TIME_OF_DAY_REGEX));
}
@Test
- public void testRoundToNearestThreshold_roundsCorrectly() {
+ public void roundTimeToNearestThreshold_roundsCorrectly() {
// test some pretty normal values
assertThat(PowerUtil.roundTimeToNearestThreshold(1200, 1000)).isEqualTo(1000);
assertThat(PowerUtil.roundTimeToNearestThreshold(800, 1000)).isEqualTo(1000);
@@ -89,4 +85,17 @@
assertThat(PowerUtil.roundTimeToNearestThreshold(-120, 100)).isEqualTo(100);
assertThat(PowerUtil.roundTimeToNearestThreshold(-200, -75)).isEqualTo(225);
}
+
+ @Test
+ public void getTargetTimeShortString_returnsTimeShortString() {
+ mContext.getSystemService(AlarmManager.class).setTimeZone("UTC");
+ mContext.getResources().getConfiguration().setLocale(Locale.US);
+ var currentTimeMs = Instant.parse("2024-06-06T15:00:00Z").toEpochMilli();
+ var remainingTimeMs = Duration.ofMinutes(30).toMillis();
+
+ var actualTimeString =
+ PowerUtil.getTargetTimeShortString(mContext, remainingTimeMs, currentTimeMs);
+
+ assertThat(actualTimeString).isEqualTo("3:30 PM");
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
index c7e96bc..00e4772 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
@@ -38,6 +38,8 @@
private List<BluetoothDevice> mMostRecentlyConnectedDevices;
private BluetoothProfile.ServiceListener mServiceListener;
private ParcelUuid[] mParcelUuids;
+ private int mIsLeAudioBroadcastSourceSupported;
+ private int mIsLeAudioBroadcastAssistantSupported;
@Implementation
protected boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
@@ -97,4 +99,22 @@
public void setUuids(ParcelUuid[] uuids) {
mParcelUuids = uuids;
}
+
+ @Implementation
+ protected int isLeAudioBroadcastSourceSupported() {
+ return mIsLeAudioBroadcastSourceSupported;
+ }
+
+ public void setIsLeAudioBroadcastSourceSupported(int isSupported) {
+ mIsLeAudioBroadcastSourceSupported = isSupported;
+ }
+
+ @Implementation
+ protected int isLeAudioBroadcastAssistantSupported() {
+ return mIsLeAudioBroadcastAssistantSupported;
+ }
+
+ public void setIsLeAudioBroadcastAssistantSupported(int isSupported) {
+ mIsLeAudioBroadcastAssistantSupported = isSupported;
+ }
}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 17d9f1b..097840e 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -338,4 +338,7 @@
<!-- Value to use as default scale for fonts -->
<item name="def_device_font_scale" format="float" type="dimen">1.0</item>
+
+ <!-- The default ringer mode. See `AudioManager` for list of valid values. -->
+ <integer name="def_ringer_mode">2</integer>
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index ad3eb92..e77cf2f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -531,13 +531,22 @@
pw.println(" put NAMESPACE KEY VALUE [default]");
pw.println(" Change the contents of KEY to VALUE for the given NAMESPACE.");
pw.println(" {default} to set as the default value.");
+ pw.println(" override NAMESPACE KEY VALUE");
+ pw.println(" Set flag NAMESPACE/KEY to the given VALUE, and ignores "
+ + "server-updates for");
+ pw.println(" this flag. This can still be called even if there is no underlying "
+ + "value set.");
pw.println(" delete NAMESPACE KEY");
pw.println(" Delete the entry for KEY for the given NAMESPACE.");
+ pw.println(" clear_override NAMESPACE KEY");
+ pw.println(" Clear local sticky flag override for KEY in the given NAMESPACE.");
pw.println(" list_namespaces [--public]");
pw.println(" Prints the name of all (or just the public) namespaces.");
pw.println(" list [NAMESPACE]");
pw.println(" Print all keys and values defined, optionally for the given "
+ "NAMESPACE.");
+ pw.println(" list_local_overrides");
+ pw.println(" Print all flags that have been overridden.");
pw.println(" reset RESET_MODE [NAMESPACE]");
pw.println(" Reset all flag values, optionally for a NAMESPACE, according to "
+ "RESET_MODE.");
@@ -547,8 +556,9 @@
+ "flags are reset");
pw.println(" set_sync_disabled_for_tests SYNC_DISABLED_MODE");
pw.println(" Modifies bulk property setting behavior for tests. When in one of the"
- + " disabled modes this ensures that config isn't overwritten.");
- pw.println(" SYNC_DISABLED_MODE is one of:");
+ + " disabled modes");
+ pw.println(" this ensures that config isn't overwritten. SYNC_DISABLED_MODE is "
+ + "one of:");
pw.println(" none: Sync is not disabled. A reboot may be required to restart"
+ " syncing.");
pw.println(" persistent: Sync is disabled, this state will survive a reboot.");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1ead14a..096cccc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3943,8 +3943,10 @@
globalSettings.updateSettingLocked(Settings.Global.ZEN_MODE,
Integer.toString(Settings.Global.ZEN_MODE_OFF), null,
true, SettingsState.SYSTEM_PACKAGE_NAME);
+ final int defaultRingerMode =
+ getContext().getResources().getInteger(R.integer.def_ringer_mode);
globalSettings.updateSettingLocked(Settings.Global.MODE_RINGER,
- Integer.toString(AudioManager.RINGER_MODE_NORMAL), null,
+ Integer.toString(defaultRingerMode), null,
true, SettingsState.SYSTEM_PACKAGE_NAME);
}
currentVersion = 119;
diff --git a/packages/Shell/res/values-af/strings.xml b/packages/Shell/res/values-af/strings.xml
index b52a83c..90f0962 100644
--- a/packages/Shell/res/values-af/strings.xml
+++ b/packages/Shell/res/values-af/strings.xml
@@ -35,7 +35,7 @@
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Kon nie foutverslagbesonderhede by ZIP-lêer voeg nie"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"naamloos"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"Besonderhede"</string>
- <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Skermkiekie"</string>
+ <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Skermskoot"</string>
<string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Skermkiekie is suksesvol geneem."</string>
<string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Kon nie skermkiekie neem nie."</string>
<string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Foutverslag <xliff:g id="ID">#%d</xliff:g> se besonderhede"</string>
diff --git a/packages/SimAppDialog/res/values-pt-rBR/strings.xml b/packages/SimAppDialog/res/values-pt-rBR/strings.xml
index abf8dbd..fe31aa0 100644
--- a/packages/SimAppDialog/res/values-pt-rBR/strings.xml
+++ b/packages/SimAppDialog/res/values-pt-rBR/strings.xml
@@ -22,5 +22,5 @@
<string name="install_carrier_app_description" msgid="4014303558674923797">"Para que o novo chip funcione corretamente, instale o app <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="install_carrier_app_description_default" msgid="7356830245205847840">"Para que o novo chip funcione corretamente, instale o app da operadora"</string>
<string name="install_carrier_app_defer_action" msgid="2558576736886876209">"Agora não"</string>
- <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Fazer o download do app"</string>
+ <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Baixar o app"</string>
</resources>
diff --git a/packages/SimAppDialog/res/values-pt/strings.xml b/packages/SimAppDialog/res/values-pt/strings.xml
index abf8dbd..fe31aa0 100644
--- a/packages/SimAppDialog/res/values-pt/strings.xml
+++ b/packages/SimAppDialog/res/values-pt/strings.xml
@@ -22,5 +22,5 @@
<string name="install_carrier_app_description" msgid="4014303558674923797">"Para que o novo chip funcione corretamente, instale o app <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="install_carrier_app_description_default" msgid="7356830245205847840">"Para que o novo chip funcione corretamente, instale o app da operadora"</string>
<string name="install_carrier_app_defer_action" msgid="2558576736886876209">"Agora não"</string>
- <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Fazer o download do app"</string>
+ <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Baixar o app"</string>
</resources>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 40db52e..c88c373 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -117,6 +117,7 @@
"SystemUILogLib",
"SystemUIPluginLib",
"SystemUISharedLib",
+ "SystemUI-shared-utils",
"SystemUI-statsd",
"SettingsLib",
"com_android_systemui_flags_lib",
@@ -263,6 +264,7 @@
"SystemUISharedLib",
"SystemUICustomizationLib",
"SystemUICustomizationTestUtils",
+ "SystemUI-shared-utils",
"SystemUI-statsd",
"SettingsLib",
"com_android_systemui_flags_lib",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index bbc9fe4..561c85e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -367,6 +367,9 @@
<uses-permission android:name="android.permission.MONITOR_STICKY_MODIFIER_STATE" />
+ <!-- To follow the grammatical gender preference -->
+ <uses-permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" />
+
<!-- Listen to (dis-)connection of external displays and enable / disable them. -->
<uses-permission android:name="android.permission.MANAGE_DISPLAYS" />
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-af/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-af/strings.xml
index cbb5ad7..56fd06c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-af/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-af/strings.xml
@@ -12,7 +12,7 @@
<string name="lockscreen_label" msgid="648347953557887087">"Sluitskerm"</string>
<string name="quick_settings_label" msgid="2999117381487601865">"Kitsinstellings"</string>
<string name="notifications_label" msgid="6829741046963013567">"Kennisgewings"</string>
- <string name="screenshot_label" msgid="863978141223970162">"Skermkiekie"</string>
+ <string name="screenshot_label" msgid="863978141223970162">"Skermskoot"</string>
<string name="screenshot_utterance" msgid="1430760563401895074">"Neem skermkiekie"</string>
<string name="volume_up_label" msgid="8592766918780362870">"Volume harder"</string>
<string name="volume_down_label" msgid="8574981863656447346">"Volume sagter"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-cs/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-cs/strings.xml
index e5dd693..15eb928 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-cs/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-cs/strings.xml
@@ -21,7 +21,7 @@
<string name="previous_button_content_description" msgid="840869171117765966">"Zpět na předchozí obrazovku"</string>
<string name="next_button_content_description" msgid="6810058269847364406">"Přejít na další obrazovku"</string>
<string name="accessibility_menu_description" msgid="4458354794093858297">"Nabídka usnadnění přístupu zobrazuje na obrazovce velkou nabídku k ovládání zařízení. Můžete zamknout zařízení, upravit hlasitost a jas, pořídit snímek obrazovky apod."</string>
- <string name="accessibility_menu_summary" msgid="340071398148208130">"Ovládat zařízení pomocí velké nabídky"</string>
+ <string name="accessibility_menu_summary" msgid="340071398148208130">"Ovládání zařízení pomocí velké nabídky"</string>
<string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Nastavení nabídky usnadnění přístupu"</string>
<string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Velká tlačítka"</string>
<string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Zvětšit tlačítka v nabídce přístupnosti"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-fi/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-fi/strings.xml
index 5e31739..8f2abac 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-fi/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-fi/strings.xml
@@ -21,7 +21,7 @@
<string name="previous_button_content_description" msgid="840869171117765966">"Siirry edelliselle näytöllä"</string>
<string name="next_button_content_description" msgid="6810058269847364406">"Siirry seuraavalle näytölle"</string>
<string name="accessibility_menu_description" msgid="4458354794093858297">"Saavutettavuusvalikko on suuri näyttövalikko, josta voit ohjata laitettasi. Voit esimerkiksi lukita laitteen, säätää äänenvoimakkuutta ja kirkkautta sekä ottaa kuvakaappauksia."</string>
- <string name="accessibility_menu_summary" msgid="340071398148208130">"Ohjaa laitetta suurella valikolla"</string>
+ <string name="accessibility_menu_summary" msgid="340071398148208130">"Ohjaa laitetta suuren valikon avulla"</string>
<string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Saavutettavuusvalikon asetukset"</string>
<string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Suuret painikkeet"</string>
<string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Suurenna saavutettavuusvalikon painikkeita"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-hi/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-hi/strings.xml
index 1cb9b5e..3d59c0b 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-hi/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-hi/strings.xml
@@ -2,7 +2,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="accessibility_menu_service_name" msgid="730136711554740131">"सुलभता मेन्यू"</string>
- <string name="accessibility_menu_intro" msgid="3164193281544042394">"सुलभता मेन्यू, स्क्रीन पर दिखने वाला एक बड़ा मेन्यू होता है. इसकी मदद से, अपने डिवाइस को कंट्रोल किया जा सकता है. इस मेन्यू में जाकर, अपना डिवाइस लॉक करने, स्क्रीनशॉट लेने, स्क्रीन की रोशनी और आवाज़ कंट्रोल करने जैसे कई दूसरे काम किए जा सकते हैं."</string>
+ <string name="accessibility_menu_intro" msgid="3164193281544042394">"सुलभता मेन्यू, डिवाइस की स्क्रीन पर दिखने वाला एक बड़ा मेन्यू होता है. इस मेन्यू में जाकर, डिवाइस को लॉक करने, स्क्रीनशॉट लेने, स्क्रीन की रोशनी और आवाज़ को कंट्रोल करने जैसे कई काम किए जा सकते हैं."</string>
<string name="assistant_label" msgid="6796392082252272356">"Assistant"</string>
<string name="assistant_utterance" msgid="65509599221141377">"Assistant"</string>
<string name="a11y_settings_label" msgid="3977714687248445050">"सुलभता सेटिंग"</string>
@@ -20,7 +20,7 @@
<string name="brightness_down_label" msgid="7115662941913272072">"स्क्रीन की रोशनी कम करें"</string>
<string name="previous_button_content_description" msgid="840869171117765966">"पिछली स्क्रीन पर जाएं"</string>
<string name="next_button_content_description" msgid="6810058269847364406">"अगली स्क्रीन पर जाएं"</string>
- <string name="accessibility_menu_description" msgid="4458354794093858297">"सुलभता मेन्यू, स्क्रीन पर दिखने वाला एक बड़ा मेन्यू होता है. इसकी मदद से, अपने डिवाइस को कंट्रोल किया जा सकता है. इस मेन्यू में जाकर, अपना डिवाइस लॉक करने, स्क्रीनशॉट लेने, स्क्रीन की रोशनी और आवाज़ कंट्रोल करने जैसे कई दूसरे काम किए जा सकते हैं."</string>
+ <string name="accessibility_menu_description" msgid="4458354794093858297">"सुलभता मेन्यू, डिवाइस की स्क्रीन पर दिखने वाला एक बड़ा मेन्यू होता है. इस मेन्यू में जाकर, डिवाइस को लॉक करने, स्क्रीनशॉट लेने, स्क्रीन की रोशनी और आवाज़ को कंट्रोल करने जैसे कई काम किए जा सकते हैं."</string>
<string name="accessibility_menu_summary" msgid="340071398148208130">"बड़े मेन्यू की मदद से डिवाइस को कंट्रोल करें"</string>
<string name="accessibility_menu_settings_name" msgid="1716888058785672611">"सुलभता मेन्यू सेटिंग"</string>
<string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"बड़े बटन"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-ky/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-ky/strings.xml
index fa8b587..98c91ed 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-ky/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-ky/strings.xml
@@ -21,7 +21,7 @@
<string name="previous_button_content_description" msgid="840869171117765966">"Мурунку экранга өтүү"</string>
<string name="next_button_content_description" msgid="6810058269847364406">"Кийинки экранга өтүү"</string>
<string name="accessibility_menu_description" msgid="4458354794093858297">"Атайын мүмкүнчүлүктөр менюсу — бул түзмөгүңүздү көзөмөлдөөнү жеңилдетүүгө ылайыкташтырылган экрандагы чоң меню. Түзмөгүңүздү кулпулап, үнүнүн катуулугун жана экрандын жарыктыгын көзөмөлдөп, скриншотторду тартып жана башка аракеттерди аткара аласыз."</string>
- <string name="accessibility_menu_summary" msgid="340071398148208130">"Түзмөктү чоң менюдан башкаруу"</string>
+ <string name="accessibility_menu_summary" msgid="340071398148208130">"Түзмөктү чоң менюдан башкарасыз"</string>
<string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Атайын мүмкүнчүлүктөр менюсунун параметрлери"</string>
<string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Чоң баскычтар"</string>
<string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Атайын мүмкүнчүлүктөр менюсундагы баскычтардын өлчөмүн чоңойтот"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-ms/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-ms/strings.xml
index 9c1ea75..fd34732 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-ms/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-ms/strings.xml
@@ -2,14 +2,14 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="accessibility_menu_service_name" msgid="730136711554740131">"Menu Kebolehaksesan"</string>
- <string name="accessibility_menu_intro" msgid="3164193281544042394">"Menu Kebolehaksesan menyediakan menu pada skrin yang besar untuk mengawal peranti anda. Anda boleh mengunci peranti anda, mengawal kelantangan dan kecerahan, mengambil tangkapan skrin dan banyak lagi."</string>
+ <string name="accessibility_menu_intro" msgid="3164193281544042394">"Menu Kebolehaksesan menyediakan menu yang besar pada skrin untuk mengawal peranti anda. Anda boleh mengunci peranti, mengawal kelantangan dan kecerahan, mengambil tangkapan skrin dan banyak lagi."</string>
<string name="assistant_label" msgid="6796392082252272356">"Assistant"</string>
<string name="assistant_utterance" msgid="65509599221141377">"Assistant"</string>
<string name="a11y_settings_label" msgid="3977714687248445050">"Tetapan Kebolehaksesan"</string>
<string name="power_label" msgid="7699720321491287839">"Kuasa"</string>
<string name="power_utterance" msgid="7444296686402104807">"Pilihan kuasa"</string>
<string name="recent_apps_label" msgid="6583276995616385847">"Apl terbaharu"</string>
- <string name="lockscreen_label" msgid="648347953557887087">"Kunci skrin"</string>
+ <string name="lockscreen_label" msgid="648347953557887087">"Skrin kunci"</string>
<string name="quick_settings_label" msgid="2999117381487601865">"Tetapan Pantas"</string>
<string name="notifications_label" msgid="6829741046963013567">"Pemberitahuan"</string>
<string name="screenshot_label" msgid="863978141223970162">"Tangkapan skrin"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-nl/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-nl/strings.xml
index a8d6a0b..3f72d95 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-nl/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-nl/strings.xml
@@ -2,7 +2,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="accessibility_menu_service_name" msgid="730136711554740131">"Toegankelijkheidsmenu"</string>
- <string name="accessibility_menu_intro" msgid="3164193281544042394">"Het toegankelijkheidsmenu is een groot menu op het scherm waarmee je je apparaat kunt bedienen. Je kunt onder meer je apparaat vergrendelen, het volume en de helderheid beheren en screenshots maken."</string>
+ <string name="accessibility_menu_intro" msgid="3164193281544042394">"Het toegankelijkheidsmenu is een groot menu op het scherm waarmee je je apparaat kunt bedienen. Je kunt onder meer je apparaat vergrendelen, het volume en de helderheid aanpassen en screenshots maken."</string>
<string name="assistant_label" msgid="6796392082252272356">"Assistent"</string>
<string name="assistant_utterance" msgid="65509599221141377">"Assistent"</string>
<string name="a11y_settings_label" msgid="3977714687248445050">"Instellingen voor toegankelijkheid"</string>
@@ -21,7 +21,7 @@
<string name="previous_button_content_description" msgid="840869171117765966">"Ga naar vorig scherm"</string>
<string name="next_button_content_description" msgid="6810058269847364406">"Ga naar volgend scherm"</string>
<string name="accessibility_menu_description" msgid="4458354794093858297">"Het toegankelijkheidsmenu is een groot menu op het scherm waarmee je je apparaat kunt bedienen. Je kunt onder meer je apparaat vergrendelen, het volume en de helderheid aanpassen en screenshots maken."</string>
- <string name="accessibility_menu_summary" msgid="340071398148208130">"Bedien apparaat via groot menu"</string>
+ <string name="accessibility_menu_summary" msgid="340071398148208130">"Bedien het apparaat via een groot menu"</string>
<string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Instellingen toegankelijkheidsmenu"</string>
<string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Grote knoppen"</string>
<string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Vergroot knoppen in het toegankelijkheidsmenu"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rPT/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rPT/strings.xml
index 44aff75..0cc2f58 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rPT/strings.xml
@@ -21,7 +21,7 @@
<string name="previous_button_content_description" msgid="840869171117765966">"Ir para o ecrã anterior"</string>
<string name="next_button_content_description" msgid="6810058269847364406">"Ir para o ecrã seguinte"</string>
<string name="accessibility_menu_description" msgid="4458354794093858297">"O menu Acessibilidade disponibiliza um menu grande no ecrã para controlar o dispositivo. Pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de ecrã e muito mais."</string>
- <string name="accessibility_menu_summary" msgid="340071398148208130">"Controle o dispositivo através do menu grande"</string>
+ <string name="accessibility_menu_summary" msgid="340071398148208130">"Controlar dispositivo através do menu grande"</string>
<string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Definições do menu Acessibilidade"</string>
<string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Botões grandes"</string>
<string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Aumentar o tamanho dos botões do menu Acessibilidade"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-sk/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-sk/strings.xml
index 2647498..62f63a8 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-sk/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-sk/strings.xml
@@ -2,7 +2,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="accessibility_menu_service_name" msgid="730136711554740131">"Ponuka Dostupnosť"</string>
- <string name="accessibility_menu_intro" msgid="3164193281544042394">"Ponukou dostupnosti sa rozumie veľká ponuka na obrazovke, pomocou ktorej môžete ovládať zariadenie. Môžete ho uzamknúť, ovládať hlasitosť a jas, vytvárať snímky obrazovky a mnoho ďalšieho."</string>
+ <string name="accessibility_menu_intro" msgid="3164193281544042394">"Ponukou Dostupnosť sa rozumie veľká ponuka na obrazovke, pomocou ktorej môžete ovládať zariadenie. Môžete ho uzamknúť, ovládať hlasitosť a jas, vytvárať snímky obrazovky a mnoho ďalšieho."</string>
<string name="assistant_label" msgid="6796392082252272356">"Asistent"</string>
<string name="assistant_utterance" msgid="65509599221141377">"Asistent"</string>
<string name="a11y_settings_label" msgid="3977714687248445050">"Nastavenia dostupnosti"</string>
@@ -21,7 +21,7 @@
<string name="previous_button_content_description" msgid="840869171117765966">"Prejsť na predchádzajúcu obrazovku"</string>
<string name="next_button_content_description" msgid="6810058269847364406">"Prejsť na ďalšiu obrazovku"</string>
<string name="accessibility_menu_description" msgid="4458354794093858297">"Ponuka dostupnosti spustí na obrazovke telefónu veľkú ponuku, pomocou ktorej môžete ovládať svoje zariadenie. Môžete ho uzamknúť, ovládať hlasitosť a jas, vytvárať snímky obrazovky a mnoho ďalšieho."</string>
- <string name="accessibility_menu_summary" msgid="340071398148208130">"Ovládať zariadenie pomocou veľkej ponuky"</string>
+ <string name="accessibility_menu_summary" msgid="340071398148208130">"Ovládanie zariadenia pomocou veľkej ponuky"</string>
<string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Nastavenia ponuky dostupnosti"</string>
<string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Veľké tlačidlá"</string>
<string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Zväčšiť tlačidlá ponuky dostupnosti"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
index 5f3d1ea..0ab99fa 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -30,8 +30,6 @@
import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_TOGGLE_MENU;
import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.PACKAGE_NAME;
-import static com.google.common.truth.Truth.assertThat;
-
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Instrumentation;
import android.app.KeyguardManager;
@@ -449,7 +447,10 @@
closeScreen();
wakeUpScreen();
- assertThat(isMenuVisible()).isFalse();
+ TestUtils.waitUntil("Menu did not close.",
+ TIMEOUT_UI_CHANGE_S,
+ () -> !isMenuVisible()
+ );
}
@Test
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 8137e40..14ebc39 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -32,6 +32,16 @@
}
flag {
+ name: "floating_menu_narrow_target_content_observer"
+ namespace: "accessibility"
+ description: "stops the FAB from monitoring enabled services to trigger target content changes."
+ bug: "331740049"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+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."
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 2ad9854..c61002e 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -338,6 +338,16 @@
}
flag {
+ name: "status_bar_monochrome_icons_fix"
+ namespace: "systemui"
+ description: "Fixes the status bar icon size when drawing InsetDrawables (ie. monochrome icons)"
+ bug: "329091967"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "compose_bouncer"
namespace: "systemui"
description: "Use the new compose bouncer in SystemUI"
@@ -467,10 +477,13 @@
}
flag {
- name: "screenshot_private_profile"
+ name: "screenshot_private_profile_behavior_fix"
namespace: "systemui"
description: "Private profile support for screenshots"
bug: "327613051"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -736,8 +749,48 @@
}
flag {
+ name: "remove_dream_overlay_hide_on_touch"
+ namespace: "systemui"
+ description: "Removes logic to hide the dream overlay on user interaction, as it conflicts with various transitions"
+ bug: "329091030"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "keyboard_docking_indicator"
namespace: "systemui"
description: "Glow bar indicator reveals upon keyboard docking."
bug: "324600132"
}
+
+flag {
+ name: "dream_overlay_bouncer_swipe_direction_filtering"
+ namespace: "systemui"
+ description: "do not initiate bouncer swipe when the direction is opposite of the expansion"
+ bug: "333632464"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "dream_input_session_pilfer_once"
+ namespace: "systemui"
+ description: "Pilfer at most once per input session"
+ bug: "324600132"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "slice_broadcast_relay_in_background"
+ namespace: "systemui"
+ description: "Move handling of slice broadcast relay broadcasts to background threads"
+ bug: "334767208"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index e20425d..94f8846 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -36,6 +36,7 @@
import android.view.WindowManager.TransitionOldType;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
+import android.window.RemoteTransitionStub;
import android.window.TransitionInfo;
import com.android.wm.shell.shared.CounterRotator;
@@ -69,8 +70,8 @@
}
/** Wraps a remote animation runner in a remote-transition. */
- public static IRemoteTransition.Stub wrap(IRemoteAnimationRunner runner) {
- return new IRemoteTransition.Stub() {
+ public static RemoteTransitionStub wrap(IRemoteAnimationRunner runner) {
+ return new RemoteTransitionStub() {
final ArrayMap<IBinder, Runnable> mFinishRunnables = new ArrayMap<>();
@Override
@@ -233,11 +234,6 @@
runner.onAnimationCancelled();
finishRunnable.run();
}
-
- @Override
- public void onTransitionConsumed(IBinder iBinder, boolean aborted)
- throws RemoteException {
- }
};
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
index 4d327e1..6c982a0 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
@@ -41,9 +41,9 @@
maxMarginXdp: Float,
maxMarginYdp: Float,
minScale: Float,
- translateXEasing: Interpolator = Interpolators.STANDARD_DECELERATE,
+ translateXEasing: Interpolator = Interpolators.BACK_GESTURE,
translateYEasing: Interpolator = Interpolators.LINEAR,
- scaleEasing: Interpolator = Interpolators.STANDARD_DECELERATE,
+ scaleEasing: Interpolator = Interpolators.BACK_GESTURE,
): BackAnimationSpec {
return BackAnimationSpec { backEvent, progressY, result ->
val displayMetrics = displayMetricsProvider()
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/PaintDrawCallback.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/PaintDrawCallback.kt
new file mode 100644
index 0000000..d50979c
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/PaintDrawCallback.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.surfaceeffects
+
+import android.graphics.Paint
+import android.graphics.RenderEffect
+
+/**
+ * A callback with a [Paint] object that contains shader info, which is triggered every frame while
+ * animation is playing. Note that the [Paint] object here is always the same instance.
+ *
+ * This approach is more performant than other ones because [RenderEffect] forces an intermediate
+ * render pass of the View to a texture to feed into it.
+ *
+ * The usage of this callback is as follows:
+ * <pre>{@code
+ * private var paint: Paint? = null
+ * // Override [View.onDraw].
+ * override fun onDraw(canvas: Canvas) {
+ * // RuntimeShader requires hardwareAcceleration.
+ * if (!canvas.isHardwareAccelerated) return
+ *
+ * paint?.let { canvas.drawPaint(it) }
+ * }
+ *
+ * // Given that this is called [PaintDrawCallback.onDraw]
+ * fun draw(paint: Paint) {
+ * this.paint = paint
+ *
+ * // Must call invalidate to trigger View#onDraw
+ * invalidate()
+ * }
+ * }</pre>
+ *
+ * Please refer to [RenderEffectDrawCallback] for alternative approach.
+ */
+interface PaintDrawCallback {
+ fun onDraw(paint: Paint)
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/RenderEffectDrawCallback.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/RenderEffectDrawCallback.kt
new file mode 100644
index 0000000..db7ee58
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/RenderEffectDrawCallback.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.surfaceeffects
+
+import android.graphics.RenderEffect
+
+/**
+ * A callback with a [RenderEffect] object that contains shader info, which is triggered every frame
+ * while animation is playing. Note that the [RenderEffect] instance is different each time to
+ * update shader uniforms.
+ *
+ * The usage of this callback is as follows:
+ * <pre>{@code
+ * private val xEffectDrawingCallback = RenderEffectDrawCallback() {
+ * val myOtherRenderEffect = createOtherRenderEffect()
+ * val chainEffect = RenderEffect.createChainEffect(renderEffect, myOtherRenderEffect)
+ * myView.setRenderEffect(chainEffect)
+ * }
+ *
+ * private val xEffect = XEffect(config, xEffectDrawingCallback)
+ * }</pre>
+ */
+interface RenderEffectDrawCallback {
+ fun onDraw(renderEffect: RenderEffect)
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffect.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffect.kt
index 1c763e8..211b84f 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffect.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffect.kt
@@ -22,6 +22,8 @@
import android.graphics.Paint
import android.graphics.RenderEffect
import android.view.View
+import com.android.systemui.surfaceeffects.PaintDrawCallback
+import com.android.systemui.surfaceeffects.RenderEffectDrawCallback
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader
@@ -334,52 +336,31 @@
)
}
- companion object {
+ /**
+ * States of the loading effect animation.
+ *
+ * <p>The state is designed to be follow the order below: [AnimationState.EASE_IN],
+ * [AnimationState.MAIN], [AnimationState.EASE_OUT]. Note that ease in and out don't necessarily
+ * mean the acceleration and deceleration in the animation curve. They simply mean each stage of
+ * the animation. (i.e. Intro, core, and rest)
+ */
+ enum class AnimationState {
+ EASE_IN,
+ MAIN,
+ EASE_OUT,
+ NOT_PLAYING
+ }
+
+ /** Optional callback that is triggered when the animation state changes. */
+ interface AnimationStateChangedCallback {
/**
- * States of the loading effect animation.
- *
- * <p>The state is designed to be follow the order below: [AnimationState.EASE_IN],
- * [AnimationState.MAIN], [AnimationState.EASE_OUT]. Note that ease in and out don't
- * necessarily mean the acceleration and deceleration in the animation curve. They simply
- * mean each stage of the animation. (i.e. Intro, core, and rest)
+ * A callback that's triggered when the [AnimationState] changes. Example usage is
+ * performing a cleanup when [AnimationState] becomes [NOT_PLAYING].
*/
- enum class AnimationState {
- EASE_IN,
- MAIN,
- EASE_OUT,
- NOT_PLAYING
- }
+ fun onStateChanged(oldState: AnimationState, newState: AnimationState) {}
+ }
- /** Client must implement one of the draw callbacks. */
- interface PaintDrawCallback {
- /**
- * A callback with a [Paint] object that contains shader info, which is triggered every
- * frame while animation is playing. Note that the [Paint] object here is always the
- * same instance.
- */
- fun onDraw(loadingPaint: Paint)
- }
-
- interface RenderEffectDrawCallback {
- /**
- * A callback with a [RenderEffect] object that contains shader info, which is triggered
- * every frame while animation is playing. Note that the [RenderEffect] instance is
- * different each time to update shader uniforms.
- */
- fun onDraw(loadingRenderEffect: RenderEffect)
- }
-
- /** Optional callback that is triggered when the animation state changes. */
- interface AnimationStateChangedCallback {
- /**
- * A callback that's triggered when the [AnimationState] changes. Example usage is
- * performing a cleanup when [AnimationState] becomes [NOT_PLAYING].
- */
- fun onStateChanged(oldState: AnimationState, newState: AnimationState) {}
- }
-
+ private companion object {
private const val MS_TO_SEC = 0.001f
-
- private val TAG = LoadingEffect::class.java.simpleName
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt
index 4a89e31..36e6909 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt
@@ -25,7 +25,6 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.DragInteraction
import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@@ -63,7 +62,6 @@
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
@@ -458,40 +456,19 @@
@Composable
fun defaultPlatformSliderColors(): PlatformSliderColors =
- if (isSystemInDarkTheme()) darkThemePlatformSliderColors()
- else lightThemePlatformSliderColors()
+ PlatformSliderColors(
+ trackColor = MaterialTheme.colorScheme.secondaryContainer,
+ indicatorColor = MaterialTheme.colorScheme.primary,
+ iconColor = MaterialTheme.colorScheme.onPrimary,
+ labelColorOnIndicator = MaterialTheme.colorScheme.onPrimary,
+ labelColorOnTrack = MaterialTheme.colorScheme.onSecondaryContainer,
+ disabledTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+ disabledIndicatorColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+ disabledIconColor = MaterialTheme.colorScheme.outline,
+ disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
}
-/** [PlatformSliderColors] for the light theme */
-@Composable
-private fun lightThemePlatformSliderColors() =
- PlatformSliderColors(
- trackColor = colorResource(android.R.color.system_accent3_200),
- indicatorColor = MaterialTheme.colorScheme.tertiary,
- iconColor = MaterialTheme.colorScheme.onTertiary,
- labelColorOnIndicator = MaterialTheme.colorScheme.onTertiary,
- labelColorOnTrack = MaterialTheme.colorScheme.onTertiaryContainer,
- disabledTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
- disabledIndicatorColor = MaterialTheme.colorScheme.surfaceContainerHighest,
- disabledIconColor = MaterialTheme.colorScheme.outline,
- disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant,
- )
-
-/** [PlatformSliderColors] for the dark theme */
-@Composable
-private fun darkThemePlatformSliderColors() =
- PlatformSliderColors(
- trackColor = colorResource(android.R.color.system_accent3_600),
- indicatorColor = MaterialTheme.colorScheme.tertiary,
- iconColor = MaterialTheme.colorScheme.onTertiary,
- labelColorOnIndicator = MaterialTheme.colorScheme.onTertiary,
- labelColorOnTrack = colorResource(android.R.color.system_accent3_900),
- disabledTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
- disabledIndicatorColor = MaterialTheme.colorScheme.surfaceContainerHighest,
- disabledIconColor = MaterialTheme.colorScheme.outline,
- disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant,
- )
-
private fun PlatformSliderColors.getTrackColor(isEnabled: Boolean): Color =
if (isEnabled) trackColor else disabledTrackColor
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index 0f3d3dc2..d55d4e4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -160,7 +160,9 @@
FoldAware(
modifier =
modifier.padding(
+ start = 32.dp,
top = 92.dp,
+ end = 32.dp,
bottom = 48.dp,
),
viewModel = viewModel,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 3ec5508..d59f1f5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -22,11 +22,8 @@
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.bouncer.ui.BouncerDialogFactory
@@ -35,9 +32,7 @@
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
object Bouncer {
object Elements {
@@ -57,13 +52,7 @@
override val key = Scenes.Bouncer
override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
- MutableStateFlow(
- mapOf(
- Back to UserActionResult(Scenes.Lockscreen),
- Swipe(SwipeDirection.Down) to UserActionResult(Scenes.Lockscreen),
- )
- )
- .asStateFlow()
+ viewModel.destinationScenes
@Composable
override fun SceneScope.Content(
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 a78c2c0..07c2d3c 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
@@ -432,12 +432,12 @@
}
}
-private const val DOT_DIAMETER_DP = 16
-private const val SELECTED_DOT_DIAMETER_DP = 24
+private const val DOT_DIAMETER_DP = 14
+private const val SELECTED_DOT_DIAMETER_DP = (DOT_DIAMETER_DP * 1.5).toInt()
private const val SELECTED_DOT_REACTION_ANIMATION_DURATION_MS = 83
private const val SELECTED_DOT_RETRACT_ANIMATION_DURATION_MS = 750
-private const val LINE_STROKE_WIDTH_DP = 16
-private const val FAILURE_ANIMATION_DOT_DIAMETER_DP = 13
+private const val LINE_STROKE_WIDTH_DP = DOT_DIAMETER_DP
+private const val FAILURE_ANIMATION_DOT_DIAMETER_DP = (DOT_DIAMETER_DP * 0.81f).toInt()
private const val FAILURE_ANIMATION_DOT_SHRINK_ANIMATION_DURATION_MS = 50
private const val FAILURE_ANIMATION_DOT_SHRINK_STAGGER_DELAY_MS = 33
private const val FAILURE_ANIMATION_DOT_REVERT_ANIMATION_DURATION = 617
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index bdd888f..356bfe2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -11,6 +11,7 @@
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.dimensionResource
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
@@ -24,10 +25,10 @@
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
-import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.SceneTransitionLayoutDataSource
@@ -41,6 +42,11 @@
}
val sceneTransitions = transitions {
+ to(CommunalScenes.Communal, key = CommunalTransitionKeys.SimpleFade) {
+ spec = tween(durationMillis = 250)
+ fade(Communal.Elements.Scrim)
+ fade(Communal.Elements.Content)
+ }
to(CommunalScenes.Communal) {
spec = tween(durationMillis = 1000)
translate(Communal.Elements.Content, Edge.Right)
@@ -69,6 +75,7 @@
viewModel: CommunalViewModel,
dataSourceDelegator: SceneDataSourceDelegator,
dialogFactory: SystemUIDialogFactory,
+ colors: CommunalColors,
) {
val coroutineScope = rememberCoroutineScope()
val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank)
@@ -129,7 +136,7 @@
emptyMap()
},
) {
- CommunalScene(viewModel, dialogFactory, modifier = modifier)
+ CommunalScene(viewModel, colors, dialogFactory, modifier = modifier)
}
}
}
@@ -137,15 +144,18 @@
/** Scene containing the glanceable hub UI. */
@Composable
private fun SceneScope.CommunalScene(
- viewModel: BaseCommunalViewModel,
+ viewModel: CommunalViewModel,
+ colors: CommunalColors,
dialogFactory: SystemUIDialogFactory,
modifier: Modifier = Modifier,
) {
+ val backgroundColor by colors.backgroundColor.collectAsState()
+
Box(
modifier =
Modifier.element(Communal.Elements.Scrim)
.fillMaxSize()
- .background(LocalAndroidColorScheme.current.outlineVariant),
+ .background(Color(backgroundColor.toArgb())),
)
Box(modifier.element(Communal.Elements.Content)) {
CommunalHub(viewModel = viewModel, dialogFactory = dialogFactory)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 32c0313..5d87a03 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -32,6 +32,7 @@
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
@@ -45,6 +46,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyGridState
@@ -101,6 +103,9 @@
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.CustomAccessibilityAction
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.text.style.TextAlign
@@ -119,6 +124,7 @@
import com.android.internal.R.dimen.system_app_widget_background_radius
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.ui.compose.Dimensions.CardOutlineWidth
import com.android.systemui.communal.ui.compose.extensions.allowGestures
import com.android.systemui.communal.ui.compose.extensions.detectLongPressGesture
@@ -223,32 +229,34 @@
.motionEventSpy { onMotionEvent(viewModel) }
},
) {
- if (!viewModel.isEditMode && isEmptyState) {
- EmptyStateCta(
- contentPadding = contentPadding,
- viewModel = viewModel,
- )
- } else {
- CommunalHubLazyGrid(
- communalContent = communalContent,
- viewModel = viewModel,
- contentPadding = contentPadding,
- contentOffset = contentOffset,
- setGridCoordinates = { gridCoordinates = it },
- updateDragPositionForRemove = { offset ->
- isDraggingToRemove =
- isPointerWithinCoordinates(
- offset = gridCoordinates?.let { it.positionInWindow() + offset },
- containerToCheck = removeButtonCoordinates
- )
- isDraggingToRemove
- },
- onOpenWidgetPicker = onOpenWidgetPicker,
- gridState = gridState,
- contentListState = contentListState,
- selectedKey = selectedKey,
- widgetConfigurator = widgetConfigurator,
- )
+ AccessibilityContainer(viewModel) {
+ if (!viewModel.isEditMode && isEmptyState) {
+ EmptyStateCta(
+ contentPadding = contentPadding,
+ viewModel = viewModel,
+ )
+ } else {
+ CommunalHubLazyGrid(
+ communalContent = communalContent,
+ viewModel = viewModel,
+ contentPadding = contentPadding,
+ contentOffset = contentOffset,
+ setGridCoordinates = { gridCoordinates = it },
+ updateDragPositionForRemove = { offset ->
+ isDraggingToRemove =
+ isPointerWithinCoordinates(
+ offset = gridCoordinates?.let { it.positionInWindow() + offset },
+ containerToCheck = removeButtonCoordinates
+ )
+ isDraggingToRemove
+ },
+ onOpenWidgetPicker = onOpenWidgetPicker,
+ gridState = gridState,
+ contentListState = contentListState,
+ selectedKey = selectedKey,
+ widgetConfigurator = widgetConfigurator,
+ )
+ }
}
// TODO(b/326060686): Remove this once keyguard indication area can persist over hub
@@ -1028,6 +1036,39 @@
)
}
+/** Container of the glanceable hub grid to enable accessibility actions when focused. */
+@Composable
+fun AccessibilityContainer(viewModel: BaseCommunalViewModel, content: @Composable () -> Unit) {
+ val context = LocalContext.current
+ val isFocusable by viewModel.isFocusable.collectAsState(initial = false)
+ Box(
+ modifier =
+ Modifier.fillMaxWidth().wrapContentHeight().thenIf(
+ isFocusable && !viewModel.isEditMode
+ ) {
+ Modifier.focusable(isFocusable).semantics {
+ contentDescription =
+ context.getString(
+ R.string.accessibility_content_description_for_communal_hub
+ )
+ customActions =
+ listOf(
+ CustomAccessibilityAction(
+ context.getString(
+ R.string.accessibility_action_label_close_communal_hub
+ )
+ ) {
+ viewModel.changeScene(CommunalScenes.Blank)
+ true
+ }
+ )
+ }
+ }
+ ) {
+ content()
+ }
+}
+
/**
* Returns the `contentPadding` of the grid. Use the vertical padding to push the grid content area
* below the toolbar and let the grid take the max size. This ensures the item can be dragged
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
index 55f7f69a..52cbffb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
@@ -19,8 +19,6 @@
import com.android.systemui.keyguard.ui.composable.blueprint.CommunalBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.ShortcutsBesideUdfpsBlueprintModule
-import com.android.systemui.keyguard.ui.composable.blueprint.SplitShadeWeatherClockBlueprintModule
-import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockBlueprintModule
import com.android.systemui.keyguard.ui.composable.section.OptionalSectionModule
import dagger.Module
@@ -31,8 +29,6 @@
DefaultBlueprintModule::class,
OptionalSectionModule::class,
ShortcutsBesideUdfpsBlueprintModule::class,
- SplitShadeWeatherClockBlueprintModule::class,
- WeatherClockBlueprintModule::class,
],
)
interface LockscreenSceneBlueprintModule
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
index acd9e3d..c6fe81a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
@@ -25,6 +25,9 @@
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.largeClockElementKey
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smallClockElementKey
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smartspaceElementKey
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition.transitioningToLargeClock
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition.transitioningToSmallClock
+import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockElementKeys.largeWeatherClockElementKeyList
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.ClockFaceInTransition.Companion.CLOCK_IN_MILLIS
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.ClockFaceInTransition.Companion.CLOCK_IN_START_DELAY_MILLIS
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.ClockFaceOutTransition.Companion.CLOCK_OUT_MILLIS
@@ -34,30 +37,45 @@
object ClockTransition {
val defaultClockTransitions = transitions {
from(ClockScenes.smallClockScene, to = ClockScenes.largeClockScene) {
- transitioningToLargeClock()
+ transitioningToLargeClock(largeClockElements = listOf(largeClockElementKey))
}
from(ClockScenes.largeClockScene, to = ClockScenes.smallClockScene) {
- transitioningToSmallClock()
+ transitioningToSmallClock(largeClockElements = listOf(largeClockElementKey))
}
from(ClockScenes.splitShadeLargeClockScene, to = ClockScenes.largeClockScene) {
- spec = tween(1000, easing = LinearEasing)
+ spec = tween(300, easing = LinearEasing)
+ }
+
+ from(WeatherClockScenes.largeClockScene, to = ClockScenes.smallClockScene) {
+ transitioningToSmallClock(largeClockElements = largeWeatherClockElementKeyList)
+ }
+
+ from(ClockScenes.smallClockScene, to = WeatherClockScenes.largeClockScene) {
+ transitioningToLargeClock(largeClockElements = largeWeatherClockElementKeyList)
+ }
+
+ from(
+ WeatherClockScenes.largeClockScene,
+ to = WeatherClockScenes.splitShadeLargeClockScene
+ ) {
+ spec = tween(300, easing = LinearEasing)
}
}
- private fun TransitionBuilder.transitioningToLargeClock() {
+ private fun TransitionBuilder.transitioningToLargeClock(largeClockElements: List<ElementKey>) {
spec = tween(durationMillis = STATUS_AREA_MOVE_UP_MILLIS.toInt())
timestampRange(
startMillis = CLOCK_IN_START_DELAY_MILLIS.toInt(),
endMillis = (CLOCK_IN_START_DELAY_MILLIS + CLOCK_IN_MILLIS).toInt()
) {
- fade(largeClockElementKey)
+ largeClockElements.forEach { fade(it) }
}
timestampRange(endMillis = CLOCK_OUT_MILLIS.toInt()) { fade(smallClockElementKey) }
anchoredTranslate(smallClockElementKey, smartspaceElementKey)
}
- private fun TransitionBuilder.transitioningToSmallClock() {
+ private fun TransitionBuilder.transitioningToSmallClock(largeClockElements: List<ElementKey>) {
spec = tween(durationMillis = STATUS_AREA_MOVE_DOWN_MILLIS.toInt())
timestampRange(
startMillis = CLOCK_IN_START_DELAY_MILLIS.toInt(),
@@ -66,7 +84,9 @@
fade(smallClockElementKey)
}
- timestampRange(endMillis = CLOCK_OUT_MILLIS.toInt()) { fade(largeClockElementKey) }
+ timestampRange(endMillis = CLOCK_OUT_MILLIS.toInt()) {
+ largeClockElements.forEach { fade(it) }
+ }
anchoredTranslate(smallClockElementKey, smartspaceElementKey)
}
}
@@ -81,14 +101,26 @@
object ClockElementKeys {
val largeClockElementKey = ElementKey("large-clock")
val smallClockElementKey = ElementKey("small-clock")
- val weatherSmallClockElementKey = ElementKey("weather-small-clock")
val smartspaceElementKey = ElementKey("smart-space")
}
+object WeatherClockScenes {
+ val largeClockScene = SceneKey("large-weather-clock-scene")
+ val splitShadeLargeClockScene = SceneKey("split-shade-large-weather-clock-scene")
+}
+
object WeatherClockElementKeys {
val timeElementKey = ElementKey("weather-large-clock-time")
val dateElementKey = ElementKey("weather-large-clock-date")
val weatherIconElementKey = ElementKey("weather-large-clock-weather-icon")
val temperatureElementKey = ElementKey("weather-large-clock-temperature")
val dndAlarmElementKey = ElementKey("weather-large-clock-dnd-alarm")
+ val largeWeatherClockElementKeyList =
+ listOf(
+ timeElementKey,
+ dateElementKey,
+ weatherIconElementKey,
+ temperatureElementKey,
+ dndAlarmElementKey
+ )
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 28e92aa..e499c69 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -26,9 +26,11 @@
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.modifiers.padding
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
@@ -43,6 +45,7 @@
import dagger.multibindings.IntoSet
import java.util.Optional
import javax.inject.Inject
+import kotlin.math.roundToInt
/**
* Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form
@@ -68,6 +71,7 @@
val isUdfpsVisible = viewModel.isUdfpsVisible
val shouldUseSplitNotificationShade by
viewModel.shouldUseSplitNotificationShade.collectAsState()
+ val unfoldTranslations by viewModel.unfoldTranslations.collectAsState()
LockscreenLongPress(
viewModel = viewModel.longPress,
@@ -79,10 +83,25 @@
Column(
modifier = Modifier.fillMaxSize(),
) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ with(statusBarSection) {
+ StatusBar(
+ modifier =
+ Modifier.fillMaxWidth()
+ .padding(
+ horizontal = { unfoldTranslations.start.roundToInt() },
+ )
+ )
+ }
Box {
- with(topAreaSection) { DefaultClockLayout() }
+ with(topAreaSection) {
+ DefaultClockLayout(
+ modifier =
+ Modifier.graphicsLayer {
+ translationX = unfoldTranslations.start
+ }
+ )
+ }
if (shouldUseSplitNotificationShade) {
with(notificationSection) {
Notifications(
@@ -127,8 +146,18 @@
// Aligned to bottom and NOT constrained by the lock icon.
with(bottomAreaSection) {
- Shortcut(isStart = true, applyPadding = true)
- Shortcut(isStart = false, applyPadding = true)
+ Shortcut(
+ isStart = true,
+ applyPadding = true,
+ modifier =
+ Modifier.graphicsLayer { translationX = unfoldTranslations.start },
+ )
+ Shortcut(
+ isStart = false,
+ applyPadding = true,
+ modifier =
+ Modifier.graphicsLayer { translationX = unfoldTranslations.end },
+ )
}
with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
},
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index b8f00dc..9d31955 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -26,9 +26,11 @@
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.modifiers.padding
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
@@ -43,6 +45,7 @@
import dagger.multibindings.IntoSet
import java.util.Optional
import javax.inject.Inject
+import kotlin.math.roundToInt
/**
* Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form
@@ -68,6 +71,7 @@
val isUdfpsVisible = viewModel.isUdfpsVisible
val shouldUseSplitNotificationShade by
viewModel.shouldUseSplitNotificationShade.collectAsState()
+ val unfoldTranslations by viewModel.unfoldTranslations.collectAsState()
LockscreenLongPress(
viewModel = viewModel.longPress,
@@ -79,10 +83,25 @@
Column(
modifier = Modifier.fillMaxSize(),
) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ with(statusBarSection) {
+ StatusBar(
+ modifier =
+ Modifier.fillMaxWidth()
+ .padding(
+ horizontal = { unfoldTranslations.start.roundToInt() },
+ )
+ )
+ }
Box {
- with(topAreaSection) { DefaultClockLayout() }
+ with(topAreaSection) {
+ DefaultClockLayout(
+ modifier =
+ Modifier.graphicsLayer {
+ translationX = unfoldTranslations.start
+ },
+ )
+ }
if (shouldUseSplitNotificationShade) {
with(notificationSection) {
Notifications(
@@ -111,12 +130,26 @@
}
// Constrained to the left of the lock icon (in left-to-right layouts).
- with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) }
+ with(bottomAreaSection) {
+ Shortcut(
+ isStart = true,
+ applyPadding = false,
+ modifier =
+ Modifier.graphicsLayer { translationX = unfoldTranslations.start },
+ )
+ }
with(lockSection) { LockIcon() }
// Constrained to the right of the lock icon (in left-to-right layouts).
- with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) }
+ with(bottomAreaSection) {
+ Shortcut(
+ isStart = false,
+ applyPadding = false,
+ modifier =
+ Modifier.graphicsLayer { translationX = unfoldTranslations.end },
+ )
+ }
// Aligned to bottom and constrained to below the lock icon.
Column(modifier = Modifier.fillMaxWidth()) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
deleted file mode 100644
index cba5453..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
+++ /dev/null
@@ -1,499 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.composable.blueprint
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.IntRect
-import androidx.compose.ui.unit.dp
-import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.padding
-import com.android.keyguard.KeyguardClockSwitch.LARGE
-import com.android.systemui.Flags
-import com.android.systemui.customization.R as customizationR
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.WEATHER_CLOCK_BLUEPRINT_ID
-import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
-import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
-import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
-import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
-import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
-import com.android.systemui.keyguard.ui.composable.section.NotificationSection
-import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
-import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
-import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
-import com.android.systemui.keyguard.ui.composable.section.WeatherClockSection
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.res.R
-import com.android.systemui.shade.LargeScreenHeaderHelper
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.IntoSet
-import java.util.Optional
-import javax.inject.Inject
-
-class WeatherClockBlueprint
-@Inject
-constructor(
- private val viewModel: LockscreenContentViewModel,
- private val statusBarSection: StatusBarSection,
- private val weatherClockSection: WeatherClockSection,
- private val smartSpaceSection: SmartSpaceSection,
- private val notificationSection: NotificationSection,
- private val lockSection: LockSection,
- private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
- private val bottomAreaSection: BottomAreaSection,
- private val settingsMenuSection: SettingsMenuSection,
- private val clockInteractor: KeyguardClockInteractor,
- private val mediaCarouselSection: MediaCarouselSection,
- private val clockViewModel: KeyguardClockViewModel,
-) : ComposableLockscreenSceneBlueprint {
-
- override val id: String = WEATHER_CLOCK_BLUEPRINT_ID
- @Composable
- override fun SceneScope.Content(modifier: Modifier) {
- val isUdfpsVisible = viewModel.isUdfpsVisible
- val burnIn = rememberBurnIn(clockInteractor)
- val resources = LocalContext.current.resources
- val currentClockState = clockViewModel.currentClock.collectAsState()
- val areNotificationsVisible by viewModel.areNotificationsVisible.collectAsState()
- LockscreenLongPress(
- viewModel = viewModel.longPress,
- modifier = modifier,
- ) { onSettingsMenuPlaced ->
- Layout(
- content = {
- // Constrained to above the lock icon.
- Column(
- modifier = Modifier.fillMaxWidth(),
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- val currentClock = currentClockState.value
- val clockSize by clockViewModel.clockSize.collectAsState()
- with(weatherClockSection) {
- if (currentClock == null) {
- return@with
- }
-
- if (clockSize == LARGE) {
- Time(
- clock = currentClock,
- modifier =
- Modifier.padding(
- start =
- dimensionResource(
- customizationR.dimen.clock_padding_start
- )
- )
- )
- } else {
- SmallClock(
- burnInParams = burnIn.parameters,
- modifier =
- Modifier.align(Alignment.Start)
- .onTopPlacementChanged(burnIn.onSmallClockTopChanged),
- clock = currentClock
- )
- }
- }
- with(smartSpaceSection) {
- SmartSpace(
- burnInParams = burnIn.parameters,
- onTopChanged = burnIn.onSmartspaceTopChanged,
- modifier =
- Modifier.fillMaxWidth()
- .padding(
- top = { viewModel.getSmartSpacePaddingTop(resources) },
- )
- .padding(
- bottom =
- dimensionResource(
- R.dimen.keyguard_status_view_bottom_margin
- ),
- ),
- )
- }
-
- with(mediaCarouselSection) { MediaCarousel() }
-
- if (areNotificationsVisible) {
- with(notificationSection) {
- Notifications(
- burnInParams = burnIn.parameters,
- modifier = Modifier.fillMaxWidth().weight(weight = 1f)
- )
- }
- }
- with(weatherClockSection) {
- if (currentClock == null || clockSize != LARGE) {
- return@with
- }
- LargeClockSectionBelowSmartspace(clock = currentClock)
- }
-
- if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
-
- with(lockSection) { LockIcon() }
-
- // Aligned to bottom and constrained to below the lock icon.
- Column(modifier = Modifier.fillMaxWidth()) {
- if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) {
- IndicationArea(modifier = Modifier.fillMaxWidth())
- }
- }
-
- // Aligned to bottom and NOT constrained by the lock icon.
- with(bottomAreaSection) {
- Shortcut(isStart = true, applyPadding = true)
- Shortcut(isStart = false, applyPadding = true)
- }
- with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
- },
- modifier = Modifier.fillMaxSize(),
- ) { measurables, constraints ->
- check(measurables.size == 6)
- val aboveLockIconMeasurable = measurables[0]
- val lockIconMeasurable = measurables[1]
- val belowLockIconMeasurable = measurables[2]
- val startShortcutMeasurable = measurables[3]
- val endShortcutMeasurable = measurables[4]
- val settingsMenuMeasurable = measurables[5]
-
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
- val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
-
- val aboveLockIconPlaceable =
- aboveLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
- val belowLockIconPlaceable =
- belowLockIconMeasurable.measure(
- noMinConstraints.copy(
- maxHeight =
- (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
- )
- )
- val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
- val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
- val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
-
- layout(constraints.maxWidth, constraints.maxHeight) {
- aboveLockIconPlaceable.place(
- x = 0,
- y = 0,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
- belowLockIconPlaceable.place(
- x = 0,
- y = constraints.maxHeight - belowLockIconPlaceable.height,
- )
- startShortcutPleaceable.place(
- x = 0,
- y = constraints.maxHeight - startShortcutPleaceable.height,
- )
- endShortcutPleaceable.place(
- x = constraints.maxWidth - endShortcutPleaceable.width,
- y = constraints.maxHeight - endShortcutPleaceable.height,
- )
- settingsMenuPlaceable.place(
- x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
- y = constraints.maxHeight - settingsMenuPlaceable.height,
- )
- }
- }
- }
- }
-}
-
-class SplitShadeWeatherClockBlueprint
-@Inject
-constructor(
- private val viewModel: LockscreenContentViewModel,
- private val statusBarSection: StatusBarSection,
- private val smartSpaceSection: SmartSpaceSection,
- private val notificationSection: NotificationSection,
- private val lockSection: LockSection,
- private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
- private val bottomAreaSection: BottomAreaSection,
- private val settingsMenuSection: SettingsMenuSection,
- private val clockInteractor: KeyguardClockInteractor,
- private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
- private val weatherClockSection: WeatherClockSection,
- private val mediaCarouselSection: MediaCarouselSection,
- private val clockViewModel: KeyguardClockViewModel,
-) : ComposableLockscreenSceneBlueprint {
- override val id: String = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
-
- @Composable
- override fun SceneScope.Content(modifier: Modifier) {
- val isUdfpsVisible = viewModel.isUdfpsVisible
- val burnIn = rememberBurnIn(clockInteractor)
- val resources = LocalContext.current.resources
- val currentClockState = clockViewModel.currentClock.collectAsState()
- LockscreenLongPress(
- viewModel = viewModel.longPress,
- modifier = modifier,
- ) { onSettingsMenuPlaced ->
- Layout(
- content = {
- // Constrained to above the lock icon.
- Column(
- modifier = Modifier.fillMaxSize(),
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- Row(
- modifier = Modifier.fillMaxSize(),
- ) {
- Column(
- modifier = Modifier.fillMaxHeight().weight(weight = 1f),
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
- val currentClock = currentClockState.value
- val clockSize by clockViewModel.clockSize.collectAsState()
- with(weatherClockSection) {
- if (currentClock == null) {
- return@with
- }
-
- if (clockSize == LARGE) {
- Time(
- clock = currentClock,
- modifier =
- Modifier.align(Alignment.Start)
- .padding(
- start =
- dimensionResource(
- customizationR.dimen
- .clock_padding_start
- )
- )
- )
- } else {
- SmallClock(
- burnInParams = burnIn.parameters,
- modifier =
- Modifier.align(Alignment.Start)
- .onTopPlacementChanged(
- burnIn.onSmallClockTopChanged
- ),
- clock = currentClock,
- )
- }
- }
- with(smartSpaceSection) {
- SmartSpace(
- burnInParams = burnIn.parameters,
- onTopChanged = burnIn.onSmartspaceTopChanged,
- modifier =
- Modifier.fillMaxWidth()
- .padding(
- top = {
- viewModel.getSmartSpacePaddingTop(resources)
- },
- )
- .padding(
- bottom =
- dimensionResource(
- R.dimen
- .keyguard_status_view_bottom_margin
- )
- ),
- )
- }
-
- with(mediaCarouselSection) { MediaCarousel() }
-
- with(weatherClockSection) {
- if (currentClock == null || clockSize != LARGE) {
- return@with
- }
-
- LargeClockSectionBelowSmartspace(currentClock)
- }
- }
- with(notificationSection) {
- val splitShadeTopMargin: Dp =
- if (Flags.centralizedStatusBarHeightFix()) {
- largeScreenHeaderHelper.getLargeScreenHeaderHeight().dp
- } else {
- dimensionResource(
- id = R.dimen.large_screen_shade_header_height
- )
- }
- Notifications(
- burnInParams = burnIn.parameters,
- modifier =
- Modifier.fillMaxHeight()
- .weight(weight = 1f)
- .padding(top = splitShadeTopMargin)
- )
- }
- }
-
- if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
-
- with(lockSection) { LockIcon() }
-
- // Aligned to bottom and constrained to below the lock icon.
- Column(modifier = Modifier.fillMaxWidth()) {
- if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) {
- IndicationArea(modifier = Modifier.fillMaxWidth())
- }
- }
-
- // Aligned to bottom and NOT constrained by the lock icon.
- with(bottomAreaSection) {
- Shortcut(isStart = true, applyPadding = true)
- Shortcut(isStart = false, applyPadding = true)
- }
- with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
- },
- modifier = Modifier.fillMaxSize(),
- ) { measurables, constraints ->
- check(measurables.size == 6)
- val aboveLockIconMeasurable = measurables[0]
- val lockIconMeasurable = measurables[1]
- val belowLockIconMeasurable = measurables[2]
- val startShortcutMeasurable = measurables[3]
- val endShortcutMeasurable = measurables[4]
- val settingsMenuMeasurable = measurables[5]
-
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
- val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
-
- val aboveLockIconPlaceable =
- aboveLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
- val belowLockIconPlaceable =
- belowLockIconMeasurable.measure(
- noMinConstraints.copy(
- maxHeight =
- (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
- )
- )
- val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
- val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
- val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
-
- layout(constraints.maxWidth, constraints.maxHeight) {
- aboveLockIconPlaceable.place(
- x = 0,
- y = 0,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
- belowLockIconPlaceable.place(
- x = 0,
- y = constraints.maxHeight - belowLockIconPlaceable.height,
- )
- startShortcutPleaceable.place(
- x = 0,
- y = constraints.maxHeight - startShortcutPleaceable.height,
- )
- endShortcutPleaceable.place(
- x = constraints.maxWidth - endShortcutPleaceable.width,
- y = constraints.maxHeight - endShortcutPleaceable.height,
- )
- settingsMenuPlaceable.place(
- x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
- y = constraints.maxHeight - settingsMenuPlaceable.height,
- )
- }
- }
- }
- }
-}
-
-@Module
-interface WeatherClockBlueprintModule {
- @Binds
- @IntoSet
- fun blueprint(blueprint: WeatherClockBlueprint): ComposableLockscreenSceneBlueprint
-}
-
-@Module
-interface SplitShadeWeatherClockBlueprintModule {
- @Binds
- @IntoSet
- fun blueprint(blueprint: SplitShadeWeatherClockBlueprint): ComposableLockscreenSceneBlueprint
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
index 2a99039..238a230 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
@@ -19,6 +19,8 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.boundsInWindow
@@ -39,9 +41,12 @@
params: BurnInParameters,
isClock: Boolean = false,
): Modifier {
- val burnIn = viewModel.movement(params)
+ val translationYState = remember { mutableStateOf(0F) }
+ val copiedParams = params.copy(translationY = { translationYState.value })
+ val burnIn = viewModel.movement(copiedParams)
val translationX by burnIn.map { it.translationX.toFloat() }.collectAsState(initial = 0f)
val translationY by burnIn.map { it.translationY.toFloat() }.collectAsState(initial = 0f)
+ translationYState.value = translationY
val scaleViewModel by
burnIn
.map {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 467dbca..97d5b41 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -32,7 +32,6 @@
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.animation.view.LaunchableImageView
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
@@ -44,7 +43,6 @@
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.Flow
@@ -56,7 +54,6 @@
private val vibratorHelper: VibratorHelper,
private val indicationController: KeyguardIndicationController,
private val indicationAreaViewModel: KeyguardIndicationAreaViewModel,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
) {
/**
* Renders a single lockscreen shortcut.
@@ -164,7 +161,6 @@
transitionAlpha,
falsingManager,
vibratorHelper,
- mainImmediateDispatcher,
) {
indicationController.showTransientIndication(it)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 48684a0..9f02201 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -34,7 +34,6 @@
import com.android.keyguard.LockIconViewController
import com.android.systemui.biometrics.AuthController
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -51,14 +50,12 @@
import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
class LockSection
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
private val windowManager: WindowManager,
private val authController: AuthController,
private val featureFlags: FeatureFlagsClassic,
@@ -96,7 +93,6 @@
deviceEntryBackgroundViewModel.get(),
falsingManager.get(),
vibratorHelper.get(),
- mainImmediateDispatcher,
)
}
} else {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index f8e6341..0934b20 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -16,9 +16,11 @@
package com.android.systemui.keyguard.ui.composable.section
+import android.content.Context
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
@@ -26,6 +28,10 @@
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayout
@@ -36,6 +42,7 @@
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeLargeClockScene
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeSmallClockScene
import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition
+import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockScenes
import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import javax.inject.Inject
@@ -47,6 +54,7 @@
private val smartSpaceSection: SmartSpaceSection,
private val mediaCarouselSection: MediaCarouselSection,
private val clockSection: DefaultClockSection,
+ private val weatherClockSection: WeatherClockSection,
private val clockInteractor: KeyguardClockInteractor,
) {
@Composable
@@ -64,6 +72,10 @@
splitShadeSmallClockScene
KeyguardClockViewModel.ClockLayout.LARGE_CLOCK -> largeClockScene
KeyguardClockViewModel.ClockLayout.SMALL_CLOCK -> smallClockScene
+ KeyguardClockViewModel.ClockLayout.WEATHER_LARGE_CLOCK ->
+ WeatherClockScenes.largeClockScene
+ KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_WEATHER_LARGE_CLOCK ->
+ WeatherClockScenes.splitShadeLargeClockScene
}
SceneTransitionLayout(
@@ -86,6 +98,12 @@
scene(smallClockScene) { SmallClockWithSmartSpace() }
scene(largeClockScene) { LargeClockWithSmartSpace() }
+
+ scene(WeatherClockScenes.largeClockScene) { WeatherLargeClockWithSmartSpace() }
+
+ scene(WeatherClockScenes.splitShadeLargeClockScene) {
+ WeatherLargeClockWithSmartSpace(modifier = Modifier.fillMaxWidth(0.5f))
+ }
}
}
@@ -146,4 +164,50 @@
}
}
}
+
+ @Composable
+ private fun SceneScope.WeatherLargeClockWithSmartSpace(modifier: Modifier = Modifier) {
+ val burnIn = rememberBurnIn(clockInteractor)
+ val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState()
+ val currentClockState = clockViewModel.currentClock.collectAsState()
+
+ LaunchedEffect(isLargeClockVisible) {
+ if (isLargeClockVisible) {
+ burnIn.onSmallClockTopChanged(null)
+ }
+ }
+
+ Column(modifier = modifier) {
+ val currentClock = currentClockState.value ?: return@Column
+ with(weatherClockSection) { Time(clock = currentClock, modifier = Modifier) }
+ val density = LocalDensity.current
+ val context = LocalContext.current
+
+ with(smartSpaceSection) {
+ SmartSpace(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmartspaceTopChanged,
+ modifier =
+ Modifier.heightIn(
+ min = getDimen(context, "enhanced_smartspace_height", density)
+ )
+ )
+ }
+ with(weatherClockSection) { LargeClockSectionBelowSmartspace(clock = currentClock) }
+ }
+ }
+
+ /*
+ * Use this function to access dimen which cannot be access by R.dimen directly
+ * Currently use to access dimen from BcSmartspace
+ * @param name Name of resources
+ * @param density Density required to convert dimen from Int To Dp
+ */
+ private fun getDimen(context: Context, name: String, density: Density): Dp {
+ val res = context.packageManager.getResourcesForApplication(context.packageName)
+ val id = res.getIdentifier(name, "dimen", context.packageName)
+ var dimen: Dp
+ with(density) { dimen = (if (id == 0) 0 else res.getDimensionPixelSize(id)).toDp() }
+ return dimen
+ }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
index d358453..a7bb308ad 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.composable.section
+import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.IntrinsicSize
@@ -27,18 +28,14 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.padding
-import com.android.systemui.customization.R
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.weatherSmallClockElementKey
+import com.android.systemui.customization.R as customizationR
import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockElementKeys
-import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
-import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.plugins.clocks.ClockController
import javax.inject.Inject
@@ -55,12 +52,19 @@
clock: ClockController,
modifier: Modifier = Modifier,
) {
- WeatherElement(
- weatherClockElementViewId = R.id.weather_clock_time,
- clock = clock,
- elementKey = WeatherClockElementKeys.timeElementKey,
- modifier = modifier.wrapContentSize(),
- )
+ Row(
+ modifier =
+ Modifier.padding(
+ horizontal = dimensionResource(customizationR.dimen.clock_padding_start)
+ )
+ ) {
+ WeatherElement(
+ weatherClockElementViewId = customizationR.id.weather_clock_time,
+ clock = clock,
+ elementKey = WeatherClockElementKeys.timeElementKey,
+ modifier = modifier,
+ )
+ }
}
@Composable
@@ -69,7 +73,7 @@
modifier: Modifier = Modifier,
) {
WeatherElement(
- weatherClockElementViewId = R.id.weather_clock_date,
+ weatherClockElementViewId = customizationR.id.weather_clock_date,
clock = clock,
elementKey = WeatherClockElementKeys.dateElementKey,
modifier = modifier,
@@ -82,7 +86,7 @@
modifier: Modifier = Modifier,
) {
WeatherElement(
- weatherClockElementViewId = R.id.weather_clock_weather_icon,
+ weatherClockElementViewId = customizationR.id.weather_clock_weather_icon,
clock = clock,
elementKey = WeatherClockElementKeys.weatherIconElementKey,
modifier = modifier.wrapContentSize(),
@@ -95,7 +99,7 @@
modifier: Modifier = Modifier,
) {
WeatherElement(
- weatherClockElementViewId = R.id.weather_clock_alarm_dnd,
+ weatherClockElementViewId = customizationR.id.weather_clock_alarm_dnd,
clock = clock,
elementKey = WeatherClockElementKeys.dndAlarmElementKey,
modifier = modifier.wrapContentSize(),
@@ -108,7 +112,7 @@
modifier: Modifier = Modifier,
) {
WeatherElement(
- weatherClockElementViewId = R.id.weather_clock_temperature,
+ weatherClockElementViewId = customizationR.id.weather_clock_temperature,
clock = clock,
elementKey = WeatherClockElementKeys.temperatureElementKey,
modifier = modifier.wrapContentSize(),
@@ -126,12 +130,16 @@
content {
AndroidView(
factory = {
- val view =
- clock.largeClock.layout.views.first {
- it.id == weatherClockElementViewId
- }
- (view.parent as? ViewGroup)?.removeView(view)
- view
+ try {
+ val view =
+ clock.largeClock.layout.views.first {
+ it.id == weatherClockElementViewId
+ }
+ (view.parent as? ViewGroup)?.removeView(view)
+ view
+ } catch (e: NoSuchElementException) {
+ View(it)
+ }
},
update = {},
modifier = modifier
@@ -147,46 +155,22 @@
Row(
modifier =
Modifier.height(IntrinsicSize.Max)
- .padding(horizontal = dimensionResource(R.dimen.clock_padding_start))
+ .padding(
+ horizontal = dimensionResource(customizationR.dimen.clock_padding_start)
+ )
) {
Date(clock = clock, modifier = Modifier.wrapContentSize())
- Box(modifier = Modifier.fillMaxSize()) {
+ Box(
+ modifier =
+ Modifier.fillMaxSize()
+ .padding(
+ start = dimensionResource(customizationR.dimen.clock_padding_start)
+ )
+ ) {
Weather(clock = clock, modifier = Modifier.align(Alignment.TopStart))
Temperature(clock = clock, modifier = Modifier.align(Alignment.BottomEnd))
DndAlarmStatus(clock = clock, modifier = Modifier.align(Alignment.TopEnd))
}
}
}
-
- @Composable
- fun SceneScope.SmallClock(
- burnInParams: BurnInParameters,
- modifier: Modifier = Modifier,
- clock: ClockController,
- ) {
- val localContext = LocalContext.current
- MovableElement(key = weatherSmallClockElementKey, modifier) {
- content {
- AndroidView(
- factory = {
- val view = clock.smallClock.view
- if (view.parent != null) {
- (view.parent as? ViewGroup)?.removeView(view)
- }
- view
- },
- modifier =
- modifier
- .height(dimensionResource(R.dimen.small_clock_height))
- .padding(start = dimensionResource(R.dimen.clock_padding_start))
- .padding(top = { viewModel.getSmallClockTopMargin(localContext) })
- .burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- ),
- update = {},
- )
- }
- }
- }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 02a12e4..c6c6f57 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -407,15 +407,16 @@
AndroidView(
factory = { context ->
ModernShadeCarrierGroupMobileView.constructAndBind(
- context = context,
- logger = viewModel.mobileIconsViewModel.logger,
- slot = "mobile_carrier_shade_group",
- viewModel =
- (viewModel.mobileIconsViewModel.viewModelForSub(
- subId,
- StatusBarLocation.SHADE_CARRIER_GROUP
- ) as ShadeCarrierGroupMobileIconViewModel),
- )
+ context = context,
+ logger = viewModel.mobileIconsViewModel.logger,
+ slot = "mobile_carrier_shade_group",
+ viewModel =
+ (viewModel.mobileIconsViewModel.viewModelForSub(
+ subId,
+ StatusBarLocation.SHADE_CARRIER_GROUP
+ ) as ShadeCarrierGroupMobileIconViewModel),
+ )
+ .also { it.setOnClickListener { viewModel.onShadeCarrierGroupClicked() } }
},
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 9bd6f81..84b1a4b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -62,6 +62,7 @@
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.modifiers.padding
import com.android.compose.modifiers.thenIf
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
@@ -289,6 +290,18 @@
remember(lifecycleOwner, viewModel) { viewModel.getFooterActionsViewModel(lifecycleOwner) }
val tileSquishiness by
animateSceneFloatAsState(value = 1f, key = QuickSettings.SharedValues.TilesSquishiness)
+ val unfoldTranslationXForStartSide by
+ viewModel
+ .unfoldTranslationX(
+ isOnStartSide = true,
+ )
+ .collectAsState(0f)
+ val unfoldTranslationXForEndSide by
+ viewModel
+ .unfoldTranslationX(
+ isOnStartSide = false,
+ )
+ .collectAsState(0f)
val navBarBottomHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
val density = LocalDensity.current
@@ -337,10 +350,18 @@
modifier =
Modifier.padding(horizontal = Shade.Dimensions.HorizontalPadding)
.then(brightnessMirrorShowingModifier)
+ .padding(
+ horizontal = { unfoldTranslationXForStartSide.roundToInt() },
+ )
)
Row(modifier = Modifier.fillMaxWidth().weight(1f)) {
- Box(modifier = Modifier.weight(1f)) {
+ Box(
+ modifier =
+ Modifier.weight(1f).graphicsLayer {
+ translationX = unfoldTranslationXForStartSide
+ },
+ ) {
BrightnessMirror(
viewModel = viewModel.brightnessMirrorViewModel,
qsSceneAdapter = viewModel.qsSceneAdapter,
@@ -407,7 +428,7 @@
Modifier.weight(1f)
.fillMaxHeight()
.padding(bottom = navBarBottomHeight)
- .then(brightnessMirrorShowingModifier),
+ .then(brightnessMirrorShowingModifier)
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
index ccb5d36..fa052e8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
@@ -17,15 +17,12 @@
package com.android.systemui.volume.panel.component.anc
import com.android.systemui.volume.panel.component.anc.domain.AncAvailabilityCriteria
-import com.android.systemui.volume.panel.component.anc.ui.composable.AncPopup
-import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
-import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent
+import com.android.systemui.volume.panel.component.anc.ui.composable.AncButtonComponent
import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
import dagger.Binds
import dagger.Module
-import dagger.Provides
import dagger.multibindings.IntoMap
import dagger.multibindings.StringKey
@@ -40,14 +37,8 @@
criteria: AncAvailabilityCriteria
): ComponentAvailabilityCriteria
- companion object {
-
- @Provides
- @IntoMap
- @StringKey(VolumePanelComponents.ANC)
- fun provideVolumePanelUiComponent(
- viewModel: AncViewModel,
- popup: AncPopup,
- ): VolumePanelUiComponent = ButtonComponent(viewModel.button, popup::show)
- }
+ @Binds
+ @IntoMap
+ @StringKey(VolumePanelComponents.ANC)
+ fun bindVolumePanelUiComponent(component: AncButtonComponent): VolumePanelUiComponent
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
new file mode 100644
index 0000000..00225fc
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc.ui.composable
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import com.android.systemui.res.R
+import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
+import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
+import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
+import javax.inject.Inject
+
+class AncButtonComponent
+@Inject
+constructor(
+ private val viewModel: AncViewModel,
+ private val ancPopup: AncPopup,
+) : ComposeVolumePanelUiComponent {
+
+ @Composable
+ override fun VolumePanelComposeScope.Content(modifier: Modifier) {
+ val slice by viewModel.buttonSlice.collectAsState()
+ val label = stringResource(R.string.volume_panel_noise_control_title)
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ SliceAndroidView(
+ modifier =
+ Modifier.height(64.dp)
+ .fillMaxWidth()
+ .semantics {
+ role = Role.Button
+ contentDescription = label
+ }
+ .clip(RoundedCornerShape(28.dp)),
+ slice = slice,
+ onWidthChanged = viewModel::onButtonSliceWidthChanged,
+ onClick = { ancPopup.show(null) }
+ )
+ Text(
+ modifier = Modifier.clearAndSetSemantics {},
+ text = label,
+ style = MaterialTheme.typography.labelMedium,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
index 9f0da00..e1ee01e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
@@ -16,9 +16,6 @@
package com.android.systemui.volume.panel.component.anc.ui.composable
-import android.content.Context
-import android.view.ContextThemeWrapper
-import android.view.View
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.MaterialTheme
@@ -30,14 +27,14 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.viewinterop.AndroidView
import androidx.slice.Slice
-import androidx.slice.widget.SliceView
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.Expandable
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
import com.android.systemui.volume.panel.component.popup.ui.composable.VolumePanelPopup
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
/** ANC popup up displaying ANC control [Slice]. */
@@ -46,10 +43,12 @@
constructor(
private val volumePanelPopup: VolumePanelPopup,
private val viewModel: AncViewModel,
+ private val uiEventLogger: UiEventLogger,
) {
/** Shows a popup with the [expandable] animation. */
- fun show(expandable: Expandable) {
+ fun show(expandable: Expandable?) {
+ uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_ANC_POPUP_SHOWN)
volumePanelPopup.show(expandable, { Title() }, { Content(it) })
}
@@ -66,49 +65,18 @@
@Composable
private fun Content(dialog: SystemUIDialog) {
- val slice: Slice? by viewModel.slice.collectAsState()
+ val isAvailable by viewModel.isAvailable.collectAsState(true)
- if (slice == null) {
+ if (!isAvailable) {
SideEffect { dialog.dismiss() }
return
}
- AndroidView<SliceView>(
+ val slice by viewModel.popupSlice.collectAsState()
+ SliceAndroidView(
modifier = Modifier.fillMaxWidth(),
- factory = { context: Context ->
- SliceView(ContextThemeWrapper(context, R.style.Widget_SliceView_VolumePanel))
- .apply {
- mode = SliceView.MODE_LARGE
- isScrollable = false
- importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
- setShowTitleItems(true)
- addOnLayoutChangeListener(
- OnWidthChangedLayoutListener(viewModel::changeSliceWidth)
- )
- }
- },
- update = { sliceView: SliceView -> sliceView.slice = slice }
+ slice = slice,
+ onWidthChanged = viewModel::onPopupSliceWidthChanged
)
}
-
- private class OnWidthChangedLayoutListener(private val widthChanged: (Int) -> Unit) :
- View.OnLayoutChangeListener {
- override fun onLayoutChange(
- v: View?,
- left: Int,
- top: Int,
- right: Int,
- bottom: Int,
- oldLeft: Int,
- oldTop: Int,
- oldRight: Int,
- oldBottom: Int
- ) {
- val newWidth = right - left
- val oldWidth = oldRight - oldLeft
- if (oldWidth != newWidth) {
- widthChanged(newWidth)
- }
- }
- }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
new file mode 100644
index 0000000..f354b80
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc.ui.composable
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.ContextThemeWrapper
+import android.view.MotionEvent
+import android.view.View
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.slice.Slice
+import androidx.slice.widget.SliceView
+import com.android.systemui.res.R
+
+@Composable
+fun SliceAndroidView(
+ slice: Slice?,
+ modifier: Modifier = Modifier,
+ onWidthChanged: ((Int) -> Unit)? = null,
+ onClick: (() -> Unit)? = null,
+) {
+ AndroidView(
+ modifier = modifier,
+ factory = { context: Context ->
+ ClickableSliceView(
+ ContextThemeWrapper(context, R.style.Widget_SliceView_VolumePanel),
+ onClick,
+ )
+ .apply {
+ mode = SliceView.MODE_LARGE
+ isScrollable = false
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ setShowTitleItems(true)
+ if (onWidthChanged != null) {
+ addOnLayoutChangeListener(OnWidthChangedLayoutListener(onWidthChanged))
+ }
+ if (onClick != null) {
+ setOnClickListener { onClick() }
+ }
+ }
+ },
+ update = { sliceView: SliceView -> sliceView.slice = slice }
+ )
+}
+
+class OnWidthChangedLayoutListener(private val widthChanged: (Int) -> Unit) :
+ View.OnLayoutChangeListener {
+
+ override fun onLayoutChange(
+ v: View?,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ val newWidth = right - left
+ val oldWidth = oldRight - oldLeft
+ if (oldWidth != newWidth) {
+ widthChanged(newWidth)
+ }
+ }
+}
+
+/**
+ * [SliceView] that prioritises [onClick] when its clicked instead of passing the event to the slice
+ * first.
+ */
+@SuppressLint("ViewConstructor") // only used in this class
+private class ClickableSliceView(
+ context: Context,
+ private val onClick: (() -> Unit)?,
+) : SliceView(context) {
+
+ init {
+ if (onClick != null) {
+ setOnClickListener {}
+ }
+ }
+
+ override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
+ return onClick != null || super.onInterceptTouchEvent(ev)
+ }
+
+ override fun onClick(v: View?) {
+ onClick?.let { it() } ?: super.onClick(v)
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
index fc511e1..0893b9d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
@@ -69,9 +69,19 @@
role = Role.Button
contentDescription = label
},
- color = MaterialTheme.colorScheme.primaryContainer,
+ color =
+ if (viewModel.isActive) {
+ MaterialTheme.colorScheme.tertiaryContainer
+ } else {
+ MaterialTheme.colorScheme.surface
+ },
shape = RoundedCornerShape(28.dp),
- contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ contentColor =
+ if (viewModel.isActive) {
+ MaterialTheme.colorScheme.onTertiaryContainer
+ } else {
+ MaterialTheme.colorScheme.onSurface
+ },
onClick = onClick,
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index 780e3f2..4f3a6c8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -40,14 +40,14 @@
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.android.systemui.common.ui.compose.Icon
-import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
import kotlinx.coroutines.flow.StateFlow
/** [ComposeVolumePanelUiComponent] implementing a toggleable button from a bottom row. */
class ToggleButtonComponent(
- private val viewModelFlow: StateFlow<ToggleButtonViewModel?>,
+ private val viewModelFlow: StateFlow<ButtonViewModel?>,
private val onCheckedChange: (isChecked: Boolean) -> Unit
) : ComposeVolumePanelUiComponent {
@@ -64,10 +64,10 @@
) {
BottomComponentButtonSurface {
val colors =
- if (viewModel.isChecked) {
+ if (viewModel.isActive) {
ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.primaryContainer,
- contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+ contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
)
} else {
ButtonDefaults.buttonColors(
@@ -81,7 +81,7 @@
role = Role.Switch
contentDescription = label
},
- onClick = { onCheckedChange(!viewModel.isChecked) },
+ onClick = { onCheckedChange(!viewModel.isActive) },
shape = RoundedCornerShape(28.dp),
colors = colors
) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
index 9f9bc62..b489dfc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
@@ -59,7 +59,7 @@
* @param content is the popup body
*/
fun show(
- expandable: Expandable,
+ expandable: Expandable?,
title: @Composable (SystemUIDialog) -> Unit,
content: @Composable (SystemUIDialog) -> Unit,
) {
@@ -70,7 +70,7 @@
) {
PopupComposable(it, title, content)
}
- val controller = expandable.dialogTransitionController()
+ val controller = expandable?.dialogTransitionController()
if (controller == null) {
dialog.show()
} else {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt
index c743314..51e2064 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt
@@ -30,9 +30,11 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
@@ -119,6 +121,7 @@
) {
for (itemIndex in items.indices) {
val item = items[itemIndex]
+ val isSelected = itemIndex == scope.selectedIndex
Row(
modifier =
Modifier.height(48.dp)
@@ -126,7 +129,7 @@
.semantics {
item.contentDescription?.let { contentDescription = it }
role = Role.Switch
- selected = itemIndex == scope.selectedIndex
+ selected = isSelected
}
.clickable(
interactionSource = null,
@@ -137,7 +140,11 @@
verticalAlignment = Alignment.CenterVertically,
) {
if (item.icon !== Empty) {
- with(items[itemIndex]) { icon() }
+ CompositionLocalProvider(
+ LocalContentColor provides colors.getIconColor(isSelected)
+ ) {
+ with(items[itemIndex]) { icon() }
+ }
}
}
}
@@ -163,7 +170,10 @@
) {
val item = items[itemIndex]
if (item.icon !== Empty) {
- with(items[itemIndex]) { label() }
+ val textColor = colors.getLabelColor(itemIndex == scope.selectedIndex)
+ CompositionLocalProvider(LocalContentColor provides textColor) {
+ with(items[itemIndex]) { label() }
+ }
}
}
}
@@ -265,8 +275,22 @@
val indicatorColor: Color,
/** Color of the indicator background. */
val indicatorBackgroundColor: Color,
+ /** Color of the icon. */
+ val iconColor: Color,
+ /** Color of the icon when it's selected. */
+ val selectedIconColor: Color,
+ /** Color of the label. */
+ val labelColor: Color,
+ /** Color of the label when it's selected. */
+ val selectedLabelColor: Color,
)
+private fun VolumePanelRadioButtonBarColors.getIconColor(selected: Boolean): Color =
+ if (selected) selectedIconColor else iconColor
+
+private fun VolumePanelRadioButtonBarColors.getLabelColor(selected: Boolean): Color =
+ if (selected) selectedLabelColor else labelColor
+
object VolumePanelRadioButtonBarDefaults {
val DefaultIndicatorBackgroundPadding = 8.dp
@@ -283,12 +307,20 @@
*/
@Composable
fun defaultColors(
- indicatorColor: Color = MaterialTheme.colorScheme.primaryContainer,
+ indicatorColor: Color = MaterialTheme.colorScheme.tertiaryContainer,
indicatorBackgroundColor: Color = MaterialTheme.colorScheme.surface,
+ iconColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
+ selectedIconColor: Color = MaterialTheme.colorScheme.onTertiaryContainer,
+ labelColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
+ selectedLabelColor: Color = MaterialTheme.colorScheme.onSurface,
): VolumePanelRadioButtonBarColors =
VolumePanelRadioButtonBarColors(
indicatorColor = indicatorColor,
indicatorBackgroundColor = indicatorBackgroundColor,
+ iconColor = iconColor,
+ selectedIconColor = selectedIconColor,
+ labelColor = labelColor,
+ selectedLabelColor = selectedLabelColor,
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index eed54da..12d2bc2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.spatialaudio.ui.composable
import androidx.compose.foundation.basicMarquee
+import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -26,14 +27,15 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
-import com.android.systemui.common.ui.compose.toColor
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.volume.panel.component.popup.ui.composable.VolumePanelPopup
import com.android.systemui.volume.panel.component.selector.ui.composable.VolumePanelRadioButtonBar
import com.android.systemui.volume.panel.component.spatial.ui.viewmodel.SpatialAudioViewModel
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
class SpatialAudioPopup
@@ -41,10 +43,17 @@
constructor(
private val viewModel: SpatialAudioViewModel,
private val volumePanelPopup: VolumePanelPopup,
+ private val uiEventLogger: UiEventLogger,
) {
/** Shows a popup with the [expandable] animation. */
fun show(expandable: Expandable) {
+ uiEventLogger.logWithPosition(
+ VolumePanelUiEvent.VOLUME_PANEL_SPATIAL_AUDIO_POP_UP_SHOWN,
+ 0,
+ null,
+ viewModel.spatialAudioButtons.value.indexOfFirst { it.button.isActive }
+ )
volumePanelPopup.show(expandable, { Title() }, { Content(it) })
}
@@ -76,21 +85,16 @@
for (buttonViewModel in enabledModelStates) {
val label = buttonViewModel.button.label.toString()
item(
- isSelected = buttonViewModel.button.isChecked,
+ isSelected = buttonViewModel.button.isActive,
onItemSelected = { viewModel.setEnabled(buttonViewModel.model) },
contentDescription = label,
- icon = {
- Icon(
- icon = buttonViewModel.button.icon,
- tint = buttonViewModel.iconColor.toColor(),
- )
- },
+ icon = { Icon(icon = buttonViewModel.button.icon) },
label = {
Text(
modifier = Modifier.basicMarquee(),
text = label,
style = MaterialTheme.typography.labelMedium,
- color = buttonViewModel.labelColor.toColor(),
+ color = LocalContentColor.current,
textAlign = TextAlign.Center,
maxLines = 2
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
index f89669c..a3467f2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
@@ -85,6 +85,7 @@
onValueChange = { newValue: Float ->
sliderViewModel.onValueChanged(sliderState, newValue)
},
+ onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
sliderColors = sliderColors,
)
@@ -106,7 +107,7 @@
}
}
transition.AnimatedVisibility(
- visible = { it },
+ visible = { it || !isExpandable },
enter =
expandVertically(animationSpec = tween(durationMillis = EXPAND_DURATION_MILLIS)),
exit =
@@ -121,7 +122,7 @@
val sliderState by sliderViewModel.slider.collectAsState()
transition.AnimatedVisibility(
modifier = Modifier.padding(top = 16.dp),
- visible = { it },
+ visible = { it || !isExpandable },
enter = enterTransition(index = index, totalCount = viewModels.size),
exit = exitTransition(index = index, totalCount = viewModels.size)
) {
@@ -131,6 +132,7 @@
onValueChange = { newValue: Float ->
sliderViewModel.onValueChanged(sliderState, newValue)
},
+ onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
sliderColors = sliderColors,
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
index b284c69..bb17499 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
@@ -46,6 +46,7 @@
onValueChange = { newValue: Float ->
sliderViewModel.onValueChanged(sliderState, newValue)
},
+ onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
sliderColors = sliderColors,
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index 19d3f59..9f5ab3c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -16,13 +16,15 @@
package com.android.systemui.volume.panel.component.volume.ui.composable
+import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
-import androidx.compose.material3.LocalContentColor
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
@@ -49,6 +51,7 @@
fun VolumeSlider(
state: SliderState,
onValueChange: (newValue: Float) -> Unit,
+ onValueChangeFinished: (() -> Unit)? = null,
onIconTapped: () -> Unit,
modifier: Modifier = Modifier,
sliderColors: PlatformSliderColors,
@@ -58,7 +61,8 @@
modifier =
modifier.clearAndSetSemantics {
if (!state.isEnabled) disabled()
- contentDescription = state.label
+ contentDescription =
+ state.disabledMessage?.let { "${state.label}, $it" } ?: state.label
// provide a not animated value to the a11y because it fails to announce the
// settled value when it changes rapidly.
@@ -83,28 +87,31 @@
value = value,
valueRange = state.valueRange,
onValueChange = onValueChange,
+ onValueChangeFinished = onValueChangeFinished,
enabled = state.isEnabled,
- icon = { isDragging ->
- if (isDragging) {
- Text(text = state.valueText, color = LocalContentColor.current)
- } else {
- state.icon?.let {
- SliderIcon(
- icon = it,
- onIconTapped = onIconTapped,
- isTappable = state.isMutable,
- )
- }
+ icon = {
+ state.icon?.let {
+ SliderIcon(
+ icon = it,
+ onIconTapped = onIconTapped,
+ isTappable = state.isMutable,
+ )
}
},
colors = sliderColors,
- label = {
- VolumeSliderContent(
- modifier = Modifier,
- label = state.label,
- isEnabled = state.isEnabled,
- disabledMessage = state.disabledMessage,
- )
+ label = { isDragging ->
+ AnimatedVisibility(
+ visible = !isDragging,
+ enter = fadeIn(tween(150)),
+ exit = fadeOut(tween(150)),
+ ) {
+ VolumeSliderContent(
+ modifier = Modifier,
+ label = state.label,
+ isEnabled = state.isEnabled,
+ disabledMessage = state.disabledMessage,
+ )
+ }
}
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index 910cd5e..1bf541a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -16,7 +16,7 @@
package com.android.systemui.volume.panel.ui.composable
-import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -38,6 +38,7 @@
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.dimensionResource
@@ -75,21 +76,13 @@
modifier =
modifier
.fillMaxSize()
- .clickable(onClick = onDismiss)
+ .volumePanelClick(onDismiss)
.volumePanelPaddings(isPortrait = isPortrait),
contentAlignment = Alignment.BottomCenter,
) {
val radius = dimensionResource(R.dimen.volume_panel_corner_radius)
Surface(
- modifier =
- Modifier.clickable(
- interactionSource = null,
- indication = null,
- onClick = {
- // prevent windowCloseOnTouchOutside from dismissing when tapped
- // on the panel itself.
- },
- ),
+ modifier = Modifier.volumePanelClick {},
shape = RoundedCornerShape(topStart = radius, topEnd = radius),
color = MaterialTheme.colorScheme.surfaceContainer,
) {
@@ -185,3 +178,13 @@
)
}
}
+
+/**
+ * For some reason adding clickable modifier onto the VolumePanel affects the traversal order:
+ * b/331155283.
+ *
+ * TODO(b/334870995) revert this to Modifier.clickable
+ */
+@Composable
+private fun Modifier.volumePanelClick(onClick: () -> Unit) =
+ pointerInput(onClick) { detectTapGestures { onClick() } }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index b1cfdcf..dbec059 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -204,15 +204,16 @@
}
// Handle back events.
- // TODO(b/290184746): Make sure that this works with SystemUI once we use
- // SceneTransitionLayout in Flexiglass.
- scene(state.transitionState.currentScene).userActions[Back]?.let { result ->
- // TODO(b/290184746): Handle predictive back and use result.distance if
- // specified.
- BackHandler {
- val targetScene = result.toScene
- if (state.canChangeScene(targetScene)) {
- with(state) { coroutineScope.onChangeScene(targetScene) }
+ val targetSceneForBackOrNull =
+ scene(state.transitionState.currentScene).userActions[Back]?.toScene
+ BackHandler(
+ enabled = targetSceneForBackOrNull != null,
+ ) {
+ targetSceneForBackOrNull?.let { targetSceneForBack ->
+ // TODO(b/290184746): Handle predictive back and use result.distance if
+ // specified.
+ if (state.canChangeScene(targetSceneForBack)) {
+ with(state) { coroutineScope.onChangeScene(targetSceneForBack) }
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 1120914..ed2d20c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -50,6 +50,7 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
@@ -65,7 +66,7 @@
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.FakeSceneDataSource
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
@@ -86,7 +87,6 @@
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.GlobalSettings
import com.google.common.truth.Truth
-import dagger.Lazy
import java.util.Optional
import junit.framework.Assert
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -170,7 +170,7 @@
private lateinit var sceneInteractor: SceneInteractor
private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
private lateinit var deviceEntryInteractor: DeviceEntryInteractor
- @Mock private lateinit var primaryBouncerInteractor: Lazy<PrimaryBouncerInteractor>
+ @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
private lateinit var sceneTransitionStateFlow: MutableStateFlow<ObservableTransitionState>
private lateinit var fakeSceneDataSource: FakeSceneDataSource
@@ -209,7 +209,6 @@
val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
featureFlags = FakeFeatureFlags()
- featureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
mSetFlagsRule.enableFlags(
@@ -217,8 +216,13 @@
)
mSetFlagsRule.disableFlags(
FLAG_SIDEFPS_CONTROLLER_REFACTOR,
- AConfigFlags.FLAG_KEYGUARD_WM_STATE_REFACTOR
)
+ if (!SceneContainerFlag.isEnabled) {
+ mSetFlagsRule.disableFlags(
+ AConfigFlags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
+ AConfigFlags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT,
+ )
+ }
keyguardPasswordViewController =
KeyguardPasswordViewController(
@@ -267,7 +271,6 @@
falsingManager,
userSwitcherController,
featureFlags,
- kosmos.fakeSceneContainerFlags,
globalSettings,
sessionTracker,
Optional.of(sideFpsController),
@@ -282,7 +285,7 @@
deviceProvisionedController,
faceAuthAccessibilityDelegate,
keyguardTransitionInteractor,
- primaryBouncerInteractor,
+ { primaryBouncerInteractor },
) {
deviceEntryInteractor
}
@@ -803,17 +806,17 @@
}
@Test
+ @EnableSceneContainer
fun dismissesKeyguard_whenSceneChangesToGone() =
kosmos.testScope.runTest {
- kosmos.fakeSceneContainerFlags.enabled = true
// Upon init, we have never dismisses the keyguard.
underTest.onInit()
runCurrent()
- verify(viewMediatorCallback, never()).keyguardDone(anyInt())
+ verify(primaryBouncerInteractor, never())
+ .notifyKeyguardAuthenticatedPrimaryAuth(anyInt())
// Once the view is attached, we start listening but simply going to the bouncer scene
- // is
- // not enough to trigger a dismissal of the keyguard.
+ // is not enough to trigger a dismissal of the keyguard.
underTest.onViewAttached()
fakeSceneDataSource.pause()
sceneInteractor.changeScene(Scenes.Bouncer, "reason")
@@ -829,7 +832,8 @@
fakeSceneDataSource.unpause(expectedScene = Scenes.Bouncer)
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Bouncer)
runCurrent()
- verify(viewMediatorCallback, never()).keyguardDone(anyInt())
+ verify(primaryBouncerInteractor, never())
+ .notifyKeyguardAuthenticatedPrimaryAuth(anyInt())
// While listening, going from the bouncer scene to the gone scene, does dismiss the
// keyguard.
@@ -851,11 +855,11 @@
fakeSceneDataSource.unpause(expectedScene = Scenes.Gone)
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone)
runCurrent()
- verify(viewMediatorCallback).keyguardDone(anyInt())
+ verify(primaryBouncerInteractor).notifyKeyguardAuthenticatedPrimaryAuth(anyInt())
// While listening, moving back to the bouncer scene does not dismiss the keyguard
// again.
- clearInvocations(viewMediatorCallback)
+ clearInvocations(primaryBouncerInteractor)
fakeSceneDataSource.pause()
sceneInteractor.changeScene(Scenes.Bouncer, "reason")
sceneTransitionStateFlow.value =
@@ -870,7 +874,8 @@
fakeSceneDataSource.unpause(expectedScene = Scenes.Bouncer)
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Bouncer)
runCurrent()
- verify(viewMediatorCallback, never()).keyguardDone(anyInt())
+ verify(primaryBouncerInteractor, never())
+ .notifyKeyguardAuthenticatedPrimaryAuth(anyInt())
// Detaching the view stops listening, so moving from the bouncer scene to the gone
// scene
@@ -890,7 +895,8 @@
fakeSceneDataSource.unpause(expectedScene = Scenes.Gone)
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone)
runCurrent()
- verify(viewMediatorCallback, never()).keyguardDone(anyInt())
+ verify(primaryBouncerInteractor, never())
+ .notifyKeyguardAuthenticatedPrimaryAuth(anyInt())
// While not listening, moving to the lockscreen does not dismiss the keyguard.
fakeSceneDataSource.pause()
@@ -907,7 +913,8 @@
fakeSceneDataSource.unpause(expectedScene = Scenes.Lockscreen)
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
runCurrent()
- verify(viewMediatorCallback, never()).keyguardDone(anyInt())
+ verify(primaryBouncerInteractor, never())
+ .notifyKeyguardAuthenticatedPrimaryAuth(anyInt())
// Reattaching the view starts listening again so moving from the bouncer scene to the
// gone scene now does dismiss the keyguard again, this time from lockscreen.
@@ -926,7 +933,7 @@
fakeSceneDataSource.unpause(expectedScene = Scenes.Gone)
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone)
runCurrent()
- verify(viewMediatorCallback).keyguardDone(anyInt())
+ verify(primaryBouncerInteractor).notifyKeyguardAuthenticatedPrimaryAuth(anyInt())
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
similarity index 87%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
index 0f8fc38..04c4efb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch;
+package com.android.systemui.ambient.touch;
import static com.google.common.truth.Truth.assertThat;
@@ -31,6 +31,8 @@
import android.content.pm.UserInfo;
import android.graphics.Rect;
import android.graphics.Region;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
@@ -41,10 +43,11 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.ambient.touch.scrim.ScrimController;
+import com.android.systemui.ambient.touch.scrim.ScrimManager;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
-import com.android.systemui.dreams.touch.scrim.ScrimController;
-import com.android.systemui.dreams.touch.scrim.ScrimManager;
import com.android.systemui.settings.FakeUserTracker;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shared.system.InputChannelCompat;
@@ -85,7 +88,7 @@
FlingAnimationUtils mFlingAnimationUtilsClosing;
@Mock
- DreamTouchHandler.TouchSession mTouchSession;
+ TouchHandler.TouchSession mTouchSession;
BouncerSwipeTouchHandler mTouchHandler;
@@ -255,7 +258,7 @@
}
private static void onSessionStartHelper(BouncerSwipeTouchHandler touchHandler,
- DreamTouchHandler.TouchSession touchSession,
+ TouchHandler.TouchSession touchSession,
NotificationShadeWindowController notificationShadeWindowController) {
touchHandler.onSessionStart(touchSession);
verify(notificationShadeWindowController).setForcePluginOpen(eq(true), any());
@@ -277,6 +280,7 @@
/**
* Makes sure swiping up when bouncer initially showing doesn't change the expansion amount.
*/
+ @DisableFlags(Flags.FLAG_DREAM_OVERLAY_BOUNCER_SWIPE_DIRECTION_FILTERING)
@Test
public void testSwipeUp_whenBouncerInitiallyShowing_doesNotSetExpansion() {
when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
@@ -297,8 +301,36 @@
final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
0, 0, 0);
- assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
- .isTrue();
+ assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)).isTrue();
+
+ verify(mScrimController, never()).expand(any());
+ }
+
+ /**
+ * Makes sure swiping up when bouncer initially showing doesn't change the expansion amount.
+ */
+ @Test
+ @EnableFlags(Flags.FLAG_DREAM_OVERLAY_BOUNCER_SWIPE_DIRECTION_FILTERING)
+ public void testSwipeUp_whenBouncerInitiallyShowing_doesNotSetExpansion_directionFiltering() {
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
+
+ mTouchHandler.onSessionStart(mTouchSession);
+ ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+ verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+
+ final OnGestureListener gestureListener = gestureListenerCaptor.getValue();
+
+ final float percent = .3f;
+ final float distanceY = SCREEN_HEIGHT_PX * percent;
+
+ // Swiping up near the top of the screen where the touch initiation region is.
+ final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, distanceY, 0);
+ final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, 0, 0);
+
+ assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)).isFalse();
verify(mScrimController, never()).expand(any());
}
@@ -307,6 +339,7 @@
* Makes sure swiping down when bouncer initially hidden doesn't change the expansion amount.
*/
@Test
+ @DisableFlags(Flags.FLAG_DREAM_OVERLAY_BOUNCER_SWIPE_DIRECTION_FILTERING)
public void testSwipeDown_whenBouncerInitiallyHidden_doesNotSetExpansion() {
mTouchHandler.onSessionStart(mTouchSession);
ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
@@ -324,8 +357,34 @@
final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
0, SCREEN_HEIGHT_PX, 0);
- assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
- .isTrue();
+ assertThat(gestureListener.onScroll(event1, event2, 0, -distanceY)).isTrue();
+
+ verify(mScrimController, never()).expand(any());
+ }
+
+ /**
+ * Makes sure swiping down when bouncer initially hidden doesn't change the expansion amount.
+ */
+ @Test
+ @EnableFlags(Flags.FLAG_DREAM_OVERLAY_BOUNCER_SWIPE_DIRECTION_FILTERING)
+ public void testSwipeDown_whenBouncerInitiallyHidden_doesNotSetExpansion_directionFiltering() {
+ mTouchHandler.onSessionStart(mTouchSession);
+ ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+ verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+
+ final OnGestureListener gestureListener = gestureListenerCaptor.getValue();
+
+ final float percent = .15f;
+ final float distanceY = SCREEN_HEIGHT_PX * percent;
+
+ // Swiping down near the bottom of the screen where the touch initiation region is.
+ final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX - distanceY, 0);
+ final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX, 0);
+
+ assertThat(gestureListener.onScroll(event1, event2, 0, -distanceY)).isFalse();
verify(mScrimController, never()).expand(any());
}
@@ -444,7 +503,8 @@
0, direction == Direction.UP ? SCREEN_HEIGHT_PX - distanceY : distanceY, 0);
reset(mScrimController);
- assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
+ assertThat(gestureListener.onScroll(event1, event2, 0,
+ direction == Direction.UP ? distanceY : -distanceY))
.isTrue();
// Ensure only called once
@@ -617,8 +677,8 @@
@Test
public void testTouchSessionOnRemovedCalledTwice() {
mTouchHandler.onSessionStart(mTouchSession);
- ArgumentCaptor<DreamTouchHandler.TouchSession.Callback> onRemovedCallbackCaptor =
- ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.Callback.class);
+ ArgumentCaptor<TouchHandler.TouchSession.Callback> onRemovedCallbackCaptor =
+ ArgumentCaptor.forClass(TouchHandler.TouchSession.Callback.class);
verify(mTouchSession).registerCallback(onRemovedCallbackCaptor.capture());
onRemovedCallbackCaptor.getValue().onRemoved();
onRemovedCallbackCaptor.getValue().onRemoved();
@@ -643,7 +703,8 @@
final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
0, direction == Direction.UP ? SCREEN_HEIGHT_PX - distanceY : distanceY, 0);
- assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0, distanceY))
+ assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0,
+ direction == Direction.UP ? distanceY : -distanceY))
.isTrue();
final MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
index 6aa821f..27bffd0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.dreams.touch;
+package com.android.systemui.ambient.touch;
import static com.google.common.truth.Truth.assertThat;
@@ -52,7 +52,7 @@
ShadeViewController mShadeViewController;
@Mock
- DreamTouchHandler.TouchSession mTouchSession;
+ TouchHandler.TouchSession mTouchSession;
ShadeTouchHandler mTouchHandler;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/scrim/BouncerlessScrimControllerTest.java
similarity index 96%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/scrim/BouncerlessScrimControllerTest.java
index 7cdd478..099771c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/scrim/BouncerlessScrimControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch.scrim;
+package com.android.systemui.ambient.touch.scrim;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/scrim/ScrimManagerTest.java
similarity index 96%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/scrim/ScrimManagerTest.java
index ebbcf98..82de50c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/scrim/ScrimManagerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch.scrim;
+package com.android.systemui.ambient.touch.scrim;
import static com.google.common.truth.Truth.assertThat;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index caf9219..1cd9d76 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -32,7 +32,6 @@
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.testKosmos
@@ -86,7 +85,6 @@
AuthenticationRepositoryImpl(
applicationScope = testScope.backgroundScope,
backgroundDispatcher = kosmos.testDispatcher,
- flags = kosmos.sceneContainerFlags,
clock = clock,
getSecurityMode = getSecurityMode,
userRepository = userRepository,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 4950b96..60b48f2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -537,4 +537,65 @@
assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height())
}
}
+
+ @Test
+ fun addViewPending_layoutIsNotUpdated() =
+ testScope.runTest {
+ withReasonSuspend(REASON_AUTH_KEYGUARD) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
+ mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+
+ // GIVEN going to sleep
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.OFF,
+ to = KeyguardState.GONE,
+ testScope = this,
+ )
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ runCurrent()
+
+ // WHEN a request comes to show the view
+ controllerOverlay.show(udfpsController, overlayParams)
+ runCurrent()
+
+ // THEN the view does not get added immediately
+ verify(windowManager, never()).addView(any(), any())
+
+ // WHEN updateOverlayParams gets called when the view is pending to be added
+ controllerOverlay.updateOverlayParams(overlayParams)
+
+ // THEN the view layout is never updated
+ verify(windowManager, never()).updateViewLayout(any(), any())
+
+ // CLEANUP we hide to end the job that listens for the finishedGoingToSleep signal
+ controllerOverlay.hide()
+ }
+ }
+
+ @Test
+ fun updateOverlayParams_viewLayoutUpdated() =
+ testScope.runTest {
+ withReasonSuspend(REASON_AUTH_KEYGUARD) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ runCurrent()
+ controllerOverlay.show(udfpsController, overlayParams)
+ runCurrent()
+ verify(windowManager).addView(any(), any())
+
+ // WHEN updateOverlayParams gets called
+ controllerOverlay.updateOverlayParams(overlayParams)
+
+ // THEN the view layout is updated
+ verify(windowManager).updateViewLayout(any(), any())
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
index 741cde8..d850f17 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -29,10 +29,10 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
@@ -56,6 +56,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
class BouncerActionButtonInteractorTest : SysuiTestCase() {
@Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
@@ -75,7 +76,6 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- kosmos.fakeSceneContainerFlags.enabled = true
mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index b0d03b1..361b078 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -18,6 +18,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -25,14 +26,15 @@
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
+import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.res.R
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.seconds
@@ -48,11 +50,13 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
class BouncerInteractorTest : SysuiTestCase() {
- private val kosmos = testKosmos().apply { fakeSceneContainerFlags.enabled = true }
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val authenticationInteractor = kosmos.authenticationInteractor
+ private val uiEventLoggerFake = kosmos.uiEventLoggerFake
private lateinit var underTest: BouncerInteractor
@@ -83,6 +87,7 @@
// Thus, when auth method is sim, we expect to skip here.
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SKIPPED)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
}
@Test
@@ -104,6 +109,8 @@
// Wrong 6-digit pin
assertThat(underTest.authenticate(listOf(1, 2, 3, 5, 5, 6), tryAutoConfirm = true))
.isEqualTo(AuthenticationResult.FAILED)
+ assertThat(uiEventLoggerFake[0].eventId)
+ .isEqualTo(BouncerUiEvent.BOUNCER_PASSWORD_FAILURE.id)
// Correct input.
assertThat(
@@ -113,6 +120,9 @@
)
)
.isEqualTo(AuthenticationResult.SUCCEEDED)
+ assertThat(uiEventLoggerFake[1].eventId)
+ .isEqualTo(BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS.id)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(2)
}
@Test
@@ -148,6 +158,8 @@
// Wrong input.
assertThat(underTest.authenticate("alohamora".toList()))
.isEqualTo(AuthenticationResult.FAILED)
+ assertThat(uiEventLoggerFake[0].eventId)
+ .isEqualTo(BouncerUiEvent.BOUNCER_PASSWORD_FAILURE.id)
// Too short input.
assertThat(
@@ -165,6 +177,9 @@
// Correct input.
assertThat(underTest.authenticate("password".toList()))
.isEqualTo(AuthenticationResult.SUCCEEDED)
+ assertThat(uiEventLoggerFake[1].eventId)
+ .isEqualTo(BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS.id)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(2)
}
@Test
@@ -187,6 +202,8 @@
assertThat(wrongPattern.size)
.isAtLeast(kosmos.fakeAuthenticationRepository.minPatternLength)
assertThat(underTest.authenticate(wrongPattern)).isEqualTo(AuthenticationResult.FAILED)
+ assertThat(uiEventLoggerFake[0].eventId)
+ .isEqualTo(BouncerUiEvent.BOUNCER_PASSWORD_FAILURE.id)
// Too short input.
val tooShortPattern =
@@ -200,6 +217,9 @@
// Correct input.
assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
+ assertThat(uiEventLoggerFake[1].eventId)
+ .isEqualTo(BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS.id)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(2)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 3afca96..b83c0ce 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -18,6 +18,10 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -30,11 +34,14 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
+import com.android.systemui.truth.containsEntriesExactly
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -53,6 +60,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
class BouncerViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -63,7 +71,6 @@
@Before
fun setUp() {
- kosmos.fakeSceneContainerFlags.enabled = true
underTest = kosmos.bouncerViewModel
}
@@ -193,6 +200,23 @@
assertThat(isFoldSplitRequired).isTrue()
}
+ @Test
+ fun destinationScenes() =
+ testScope.runTest {
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ kosmos.fakeSceneDataSource.changeScene(Scenes.QuickSettings)
+ runCurrent()
+
+ kosmos.fakeSceneDataSource.changeScene(Scenes.Bouncer)
+ runCurrent()
+
+ assertThat(destinationScenes)
+ .containsEntriesExactly(
+ Back to UserActionResult(Scenes.QuickSettings),
+ Swipe(SwipeDirection.Down) to UserActionResult(Scenes.QuickSettings),
+ )
+ }
+
private fun authMethodsToTest(): List<AuthenticationMethodModel> {
return listOf(None, Pin, Password, Pattern, Sim)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 5bb36a0..256687b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -389,6 +389,61 @@
assertThat(isAnimationEnabled).isTrue()
}
+ @Test
+ fun onPinButtonClicked_whenInputSameLengthAsHintedPin_ignoresClick() =
+ testScope.runTest {
+ val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
+ kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
+ val hintedPinLength by collectLastValue(underTest.hintedPinLength)
+ assertThat(hintedPinLength).isEqualTo(FakeAuthenticationRepository.HINTING_PIN_LENGTH)
+ lockDeviceAndOpenPinBouncer()
+
+ repeat(FakeAuthenticationRepository.HINTING_PIN_LENGTH - 1) { repetition ->
+ underTest.onPinButtonClicked(repetition + 1)
+ runCurrent()
+ }
+ kosmos.fakeAuthenticationRepository.pauseCredentialChecking()
+ // If credential checking were not paused, this would check the credentials and succeed.
+ underTest.onPinButtonClicked(FakeAuthenticationRepository.HINTING_PIN_LENGTH)
+ runCurrent()
+
+ // This one should be ignored because the user has already entered a number of digits
+ // that's equal to the length of the hinting PIN length. It should result in a PIN
+ // that's exactly the same length as the hinting PIN length.
+ underTest.onPinButtonClicked(FakeAuthenticationRepository.HINTING_PIN_LENGTH + 1)
+ runCurrent()
+
+ assertThat(pin)
+ .isEqualTo(
+ buildList {
+ repeat(FakeAuthenticationRepository.HINTING_PIN_LENGTH) { index ->
+ add(index + 1)
+ }
+ }
+ )
+
+ kosmos.fakeAuthenticationRepository.unpauseCredentialChecking()
+ runCurrent()
+ assertThat(pin).isEmpty()
+ }
+
+ @Test
+ fun onPinButtonClicked_whenPinNotHinted_doesNotIgnoreClick() =
+ testScope.runTest {
+ val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
+ kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(false)
+ val hintedPinLength by collectLastValue(underTest.hintedPinLength)
+ assertThat(hintedPinLength).isNull()
+ lockDeviceAndOpenPinBouncer()
+
+ repeat(FakeAuthenticationRepository.HINTING_PIN_LENGTH + 1) { repetition ->
+ underTest.onPinButtonClicked(repetition + 1)
+ runCurrent()
+ }
+
+ assertThat(pin).hasSize(FakeAuthenticationRepository.HINTING_PIN_LENGTH + 1)
+ }
+
private fun TestScope.switchToScene(toScene: SceneKey) {
val currentScene by collectLastValue(sceneInteractor.currentScene)
val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index a944afb..76f15d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -120,19 +120,20 @@
}
@Test
- fun exitingDream_forceCommunalScene() =
+ fun occluded_forceBlankScene() =
with(kosmos) {
testScope.runTest {
val scene by collectLastValue(communalInteractor.desiredScene)
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
+ communalInteractor.changeScene(CommunalScenes.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
updateDocked(true)
fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.DREAMING,
- to = KeyguardState.LOCKSCREEN,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.OCCLUDED,
testScope = this
)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index f71121c..ce7b60e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -23,6 +23,7 @@
import android.appwidget.AppWidgetProviderInfo
import android.content.Intent
import android.content.pm.UserInfo
+import android.os.UserManager.USER_TYPE_PROFILE_MANAGED
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
@@ -59,6 +60,7 @@
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
setKeyguardFeaturesDisabled(SECONDARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
+ setKeyguardFeaturesDisabled(WORK_PROFILE, KEYGUARD_DISABLE_FEATURES_NONE)
underTest = kosmos.communalSettingsRepository
}
@@ -133,6 +135,30 @@
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
+ fun widgetsAllowedForWorkProfile_isFalse_whenDisallowedByDevicePolicy() =
+ testScope.runTest {
+ val widgetsAllowedForWorkProfile by
+ collectLastValue(underTest.getAllowedByDevicePolicy(WORK_PROFILE))
+ assertThat(widgetsAllowedForWorkProfile).isTrue()
+
+ setKeyguardFeaturesDisabled(WORK_PROFILE, KEYGUARD_DISABLE_WIDGETS_ALL)
+ assertThat(widgetsAllowedForWorkProfile).isFalse()
+ }
+
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun hubIsEnabled_whenDisallowedByDevicePolicyForWorkProfile() =
+ testScope.runTest {
+ val enabledStateForPrimaryUser by
+ collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+ assertThat(enabledStateForPrimaryUser?.enabled).isTrue()
+
+ setKeyguardFeaturesDisabled(WORK_PROFILE, KEYGUARD_DISABLE_WIDGETS_ALL)
+ assertThat(enabledStateForPrimaryUser?.enabled).isTrue()
+ }
+
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ @Test
fun hubIsDisabledByUserAndDevicePolicy() =
testScope.runTest {
val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
@@ -189,5 +215,13 @@
val PRIMARY_USER =
UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
val SECONDARY_USER = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
+ val WORK_PROFILE =
+ UserInfo(
+ 10,
+ "work",
+ /* iconPath= */ "",
+ /* flags= */ 0,
+ USER_TYPE_PROFILE_MANAGED,
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index e7ccde2..497180b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -17,6 +17,8 @@
package com.android.systemui.communal.domain.interactor
+import android.app.admin.DevicePolicyManager
+import android.app.admin.devicePolicyManager
import android.app.smartspace.SmartspaceTarget
import android.appwidget.AppWidgetProviderInfo
import android.content.Intent
@@ -32,6 +34,7 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl
import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository
@@ -49,6 +52,7 @@
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -58,7 +62,6 @@
import com.android.systemui.plugins.activityStarter
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.fakeUserTracker
@@ -71,6 +74,7 @@
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
@@ -694,10 +698,9 @@
}
@Test
+ @EnableSceneContainer
fun isCommunalShowing_whenSceneContainerEnabled() =
testScope.runTest {
- kosmos.fakeSceneContainerFlags.enabled = true
-
// Verify default is false
val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
runCurrent()
@@ -929,7 +932,6 @@
keyguardRepository.setKeyguardShowing(true)
keyguardRepository.setKeyguardOccluded(false)
tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- userRepository.setSelectedUserInfo(mainUser)
val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
userRepository.setUserInfos(userInfos)
@@ -937,6 +939,7 @@
userInfos = userInfos,
selectedUserIndex = 0,
)
+ userRepository.setSelectedUserInfo(MAIN_USER_INFO)
runCurrent()
// Widgets available.
@@ -955,7 +958,6 @@
AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
mainUser.id
)
- runCurrent()
// Only the keyguard widget is enabled.
assertThat(widgetContent).hasSize(3)
@@ -974,7 +976,6 @@
keyguardRepository.setKeyguardShowing(true)
keyguardRepository.setKeyguardOccluded(false)
tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- userRepository.setSelectedUserInfo(mainUser)
val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
userRepository.setUserInfos(userInfos)
@@ -982,6 +983,7 @@
userInfos = userInfos,
selectedUserIndex = 0,
)
+ userRepository.setSelectedUserInfo(MAIN_USER_INFO)
runCurrent()
// Widgets available.
@@ -1001,7 +1003,6 @@
AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
mainUser.id
)
- runCurrent()
// All widgets are enabled.
assertThat(widgetContent).hasSize(3)
@@ -1011,6 +1012,79 @@
}
}
+ @Test
+ fun filterWidgets_whenDisallowedByDevicePolicyForWorkProfile() =
+ testScope.runTest {
+ // Keyguard showing, and tutorial completed.
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
+ userRepository.setUserInfos(userInfos)
+ userTracker.set(
+ userInfos = userInfos,
+ selectedUserIndex = 0,
+ )
+ userRepository.setSelectedUserInfo(MAIN_USER_INFO)
+ runCurrent()
+
+ val widgetContent by collectLastValue(underTest.widgetContent)
+ // Given three widgets, and one of them is associated with work profile.
+ val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
+ val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
+ val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
+ val widgets = listOf(widget1, widget2, widget3)
+ widgetRepository.setCommunalWidgets(widgets)
+
+ setKeyguardFeaturesDisabled(
+ USER_INFO_WORK,
+ DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+ )
+
+ // Widget under work profile is filtered out and the remaining two link to main user id.
+ assertThat(widgetContent).hasSize(2)
+ widgetContent!!.forEach { model ->
+ assertThat(model.providerInfo.profile?.identifier).isEqualTo(MAIN_USER_INFO.id)
+ }
+ }
+
+ @Test
+ fun filterWidgets_whenAllowedByDevicePolicyForWorkProfile() =
+ testScope.runTest {
+ // Keyguard showing, and tutorial completed.
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
+ userRepository.setUserInfos(userInfos)
+ userTracker.set(
+ userInfos = userInfos,
+ selectedUserIndex = 0,
+ )
+ userRepository.setSelectedUserInfo(MAIN_USER_INFO)
+ runCurrent()
+
+ val widgetContent by collectLastValue(underTest.widgetContent)
+ // Given three widgets, and one of them is associated with work profile.
+ val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
+ val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
+ val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
+ val widgets = listOf(widget1, widget2, widget3)
+ widgetRepository.setCommunalWidgets(widgets)
+
+ setKeyguardFeaturesDisabled(
+ USER_INFO_WORK,
+ DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
+ )
+
+ // Widget under work profile is available.
+ assertThat(widgetContent).hasSize(3)
+ assertThat(widgetContent!![0].providerInfo.profile?.identifier)
+ .isEqualTo(USER_INFO_WORK.id)
+ }
+
private fun smartspaceTimer(id: String, timestamp: Long = 0L): SmartspaceTarget {
val timer = mock(SmartspaceTarget::class.java)
whenever(timer.smartspaceTargetId).thenReturn(id)
@@ -1020,6 +1094,15 @@
return timer
}
+ private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
+ whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
+ .thenReturn(disabledFlags)
+ kosmos.broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ )
+ }
+
private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel =
mock<CommunalWidgetContentModel> {
whenever(this.appWidgetId).thenReturn(appWidgetId)
@@ -1044,6 +1127,13 @@
private companion object {
val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
- val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE)
+ val USER_INFO_WORK =
+ UserInfo(
+ 10,
+ "work",
+ /* iconPath= */ "",
+ /* flags= */ 0,
+ UserManager.USER_TYPE_PROFILE_MANAGED,
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index ff7293d..6b2a1d5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -66,6 +66,7 @@
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testDispatcher
@@ -820,13 +821,14 @@
}
@Test
- fun isAuthenticatedIsResetToFalseWhenTransitioningToGone() =
+ fun isAuthenticatedIsResetToFalseWhenFinishedTransitioningToGoneAndStatusBarStateShade() =
testScope.runTest {
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
triggerFaceAuth(false)
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
authenticationCallback.value.onAuthenticationSucceeded(
mock(FaceManager.AuthenticationResult::class.java)
)
@@ -840,7 +842,16 @@
to = KeyguardState.GONE,
)
)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ assertThat(authenticated()).isTrue()
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
assertThat(authenticated()).isFalse()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index 5caf35b..37a6ac6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -38,6 +38,7 @@
import com.android.systemui.deviceentry.shared.model.DeviceEntryRestrictionReason.TrustAgentDisabled
import com.android.systemui.deviceentry.shared.model.DeviceEntryRestrictionReason.UnattendedUpdate
import com.android.systemui.deviceentry.shared.model.DeviceEntryRestrictionReason.UserLockdown
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.fakeSystemPropertiesHelper
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
@@ -47,7 +48,6 @@
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -62,6 +62,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
class DeviceEntryInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -74,7 +75,6 @@
@Before
fun setUp() {
- kosmos.fakeSceneContainerFlags.enabled = true
underTest = kosmos.deviceEntryInteractor
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 2af6566..41bc1dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -39,10 +39,10 @@
import com.android.dream.lowlight.LowLightTransitionCoordinator;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.complication.ComplicationHostViewController;
-import com.android.systemui.dreams.touch.scrim.BouncerlessScrimController;
import com.android.systemui.statusbar.BlurUtils;
import org.junit.Before;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
deleted file mode 100644
index c143468..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ /dev/null
@@ -1,549 +0,0 @@
-/*
- * Copyright (C) 2021 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.dreams;
-
-import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.service.dreams.IDreamOverlay;
-import android.service.dreams.IDreamOverlayCallback;
-import android.service.dreams.IDreamOverlayClient;
-import android.service.dreams.IDreamOverlayClientCallback;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.complication.ComplicationLayoutEngine;
-import com.android.systemui.dreams.complication.HideComplicationTouchHandler;
-import com.android.systemui.dreams.complication.dagger.ComplicationComponent;
-import com.android.systemui.dreams.dagger.DreamOverlayComponent;
-import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
-import com.android.systemui.touch.TouchInsetManager;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class DreamOverlayServiceTest extends SysuiTestCase {
- private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package",
- "lowlight");
-
- private static final ComponentName HOME_CONTROL_PANEL_DREAM_COMPONENT =
- new ComponentName("package", "homeControlPanel");
- private static final String DREAM_COMPONENT = "package/dream";
- private static final String WINDOW_NAME = "test";
- private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
- private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
-
- @Mock
- DreamOverlayLifecycleOwner mLifecycleOwner;
-
- @Mock
- LifecycleRegistry mLifecycleRegistry;
-
- @Rule
- public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
-
- WindowManager.LayoutParams mWindowParams;
-
- @Mock
- IDreamOverlayCallback mDreamOverlayCallback;
-
- @Mock
- WindowManagerImpl mWindowManager;
-
- @Mock
- com.android.systemui.complication.dagger.ComplicationComponent.Factory
- mComplicationComponentFactory;
-
- @Mock
- com.android.systemui.complication.dagger.ComplicationComponent mComplicationComponent;
-
- @Mock
- ComplicationLayoutEngine mComplicationVisibilityController;
-
- @Mock
- ComplicationComponent.Factory mDreamComplicationComponentFactory;
-
- @Mock
- ComplicationComponent mDreamComplicationComponent;
-
- @Mock
- HideComplicationTouchHandler mHideComplicationTouchHandler;
-
- @Mock
- DreamOverlayComponent.Factory mDreamOverlayComponentFactory;
-
- @Mock
- DreamOverlayComponent mDreamOverlayComponent;
-
- @Mock
- DreamOverlayContainerView mDreamOverlayContainerView;
-
- @Mock
- DreamOverlayContainerViewController mDreamOverlayContainerViewController;
-
- @Mock
- KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-
- @Mock
- DreamOverlayTouchMonitor mDreamOverlayTouchMonitor;
-
- @Mock
- DreamOverlayStateController mStateController;
-
- @Mock
- ViewGroup mDreamOverlayContainerViewParent;
-
- @Mock
- TouchInsetManager mTouchInsetManager;
-
- @Mock
- UiEventLogger mUiEventLogger;
-
- @Mock
- DreamOverlayCallbackController mDreamOverlayCallbackController;
-
- @Captor
- ArgumentCaptor<View> mViewCaptor;
-
- DreamOverlayService mService;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- when(mDreamOverlayComponent.getDreamOverlayContainerViewController())
- .thenReturn(mDreamOverlayContainerViewController);
- when(mLifecycleOwner.getRegistry())
- .thenReturn(mLifecycleRegistry);
- when(mDreamOverlayComponent.getDreamOverlayTouchMonitor())
- .thenReturn(mDreamOverlayTouchMonitor);
- when(mComplicationComponentFactory
- .create(any(), any(), any(), any()))
- .thenReturn(mComplicationComponent);
- when(mComplicationComponent.getVisibilityController())
- .thenReturn(mComplicationVisibilityController);
- when(mDreamComplicationComponent.getHideComplicationTouchHandler())
- .thenReturn(mHideComplicationTouchHandler);
- when(mDreamComplicationComponentFactory
- .create(any(), any()))
- .thenReturn(mDreamComplicationComponent);
- when(mDreamOverlayComponentFactory
- .create(any(), any(), any(), any()))
- .thenReturn(mDreamOverlayComponent);
- when(mDreamOverlayContainerViewController.getContainerView())
- .thenReturn(mDreamOverlayContainerView);
-
- mWindowParams = new WindowManager.LayoutParams();
- mService = new DreamOverlayService(
- mContext,
- mLifecycleOwner,
- mMainExecutor,
- mWindowManager,
- mComplicationComponentFactory,
- mDreamComplicationComponentFactory,
- mDreamOverlayComponentFactory,
- mStateController,
- mKeyguardUpdateMonitor,
- mUiEventLogger,
- mTouchInsetManager,
- LOW_LIGHT_COMPONENT,
- HOME_CONTROL_PANEL_DREAM_COMPONENT,
- mDreamOverlayCallbackController,
- WINDOW_NAME);
- }
-
- public IDreamOverlayClient getClient() throws RemoteException {
- final IBinder proxy = mService.onBind(new Intent());
- final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
- final IDreamOverlayClientCallback callback =
- Mockito.mock(IDreamOverlayClientCallback.class);
- overlay.getClient(callback);
- final ArgumentCaptor<IDreamOverlayClient> clientCaptor =
- ArgumentCaptor.forClass(IDreamOverlayClient.class);
- verify(callback).onDreamOverlayClient(clientCaptor.capture());
-
- return clientCaptor.getValue();
- }
-
- @Test
- public void testOnStartMetricsLogged() throws Exception {
- final IDreamOverlayClient client = getClient();
-
- // Inform the overlay service of dream starting.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- verify(mUiEventLogger).log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START);
- verify(mUiEventLogger).log(
- DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START);
- }
-
- @Test
- public void testOverlayContainerViewAddedToWindow() throws Exception {
- final IDreamOverlayClient client = getClient();
-
- // Inform the overlay service of dream starting.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- verify(mWindowManager).addView(any(), any());
- }
-
- // Validates that {@link DreamOverlayService} properly handles the case where the dream's
- // window is no longer valid by the time start is called.
- @Test
- public void testInvalidWindowAddStart() throws Exception {
- final IDreamOverlayClient client = getClient();
-
- doThrow(new WindowManager.BadTokenException()).when(mWindowManager).addView(any(), any());
- // Inform the overlay service of dream starting.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- verify(mWindowManager).addView(any(), any());
-
- verify(mStateController).setOverlayActive(false);
- verify(mStateController).setLowLightActive(false);
- verify(mStateController).setEntryAnimationsFinished(false);
-
- verify(mStateController, never()).setOverlayActive(true);
- verify(mUiEventLogger, never()).log(
- DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START);
-
- verify(mDreamOverlayCallbackController, never()).onStartDream();
- }
-
- @Test
- public void testDreamOverlayContainerViewControllerInitialized() throws Exception {
- final IDreamOverlayClient client = getClient();
-
- // Inform the overlay service of dream starting.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- verify(mDreamOverlayContainerViewController).init();
- }
-
- @Test
- public void testDreamOverlayContainerViewRemovedFromOldParentWhenInitialized()
- throws Exception {
- when(mDreamOverlayContainerView.getParent())
- .thenReturn(mDreamOverlayContainerViewParent)
- .thenReturn(null);
-
- final IDreamOverlayClient client = getClient();
-
- // Inform the overlay service of dream starting.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView);
- }
-
- @Test
- public void testShouldShowComplicationsSetByStartDream() throws RemoteException {
- final IDreamOverlayClient client = getClient();
-
- // Inform the overlay service of dream starting.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- true /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- assertThat(mService.shouldShowComplications()).isTrue();
- }
-
- @Test
- public void testLowLightSetByStartDream() throws RemoteException {
- final IDreamOverlayClient client = getClient();
-
- // Inform the overlay service of dream starting.
- client.startDream(mWindowParams, mDreamOverlayCallback,
- LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT);
- verify(mStateController).setLowLightActive(true);
- }
-
- @Test
- public void testHomeControlPanelSetsByStartDream() throws RemoteException {
- final IDreamOverlayClient client = getClient();
-
- // Inform the overlay service of dream starting.
- client.startDream(mWindowParams, mDreamOverlayCallback,
- HOME_CONTROL_PANEL_DREAM_COMPONENT.flattenToString(),
- false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
- assertThat(mService.getDreamComponent()).isEqualTo(HOME_CONTROL_PANEL_DREAM_COMPONENT);
- verify(mStateController).setHomeControlPanelActive(true);
- }
-
- @Test
- public void testOnEndDream() throws RemoteException {
- final IDreamOverlayClient client = getClient();
-
- // Inform the overlay service of dream starting.
- client.startDream(mWindowParams, mDreamOverlayCallback,
- LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- // Verify view added.
- verify(mWindowManager).addView(mViewCaptor.capture(), any());
-
- // Service destroyed.
- mService.onEndDream();
- mMainExecutor.runAllReady();
-
- // Verify view removed.
- verify(mWindowManager).removeView(mViewCaptor.getValue());
-
- // Verify state correctly set.
- verify(mStateController).setOverlayActive(false);
- verify(mStateController).setLowLightActive(false);
- verify(mStateController).setEntryAnimationsFinished(false);
- }
-
- @Test
- public void testImmediateEndDream() throws Exception {
- final IDreamOverlayClient client = getClient();
-
- // Start the dream, but don't execute any Runnables put on the executor yet. We delay
- // executing Runnables as the timing isn't guaranteed and we want to verify that the overlay
- // starts and finishes in the proper order even if Runnables are delayed.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- false /*shouldShowComplication*/);
- // Immediately end the dream.
- client.endDream();
- // Run any scheduled Runnables.
- mMainExecutor.runAllReady();
-
- // The overlay starts then finishes.
- InOrder inOrder = inOrder(mWindowManager);
- inOrder.verify(mWindowManager).addView(mViewCaptor.capture(), any());
- inOrder.verify(mWindowManager).removeView(mViewCaptor.getValue());
- }
-
- @Test
- public void testEndDreamDuringStartDream() throws Exception {
- final IDreamOverlayClient client = getClient();
-
- // Schedule the endDream call in the middle of the startDream implementation, as any
- // ordering is possible.
- doAnswer(invocation -> {
- client.endDream();
- return null;
- }).when(mStateController).setOverlayActive(true);
-
- // Start the dream.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- // The overlay starts then finishes.
- InOrder inOrder = inOrder(mWindowManager);
- inOrder.verify(mWindowManager).addView(mViewCaptor.capture(), any());
- inOrder.verify(mWindowManager).removeView(mViewCaptor.getValue());
- }
-
- @Test
- public void testDestroy() throws RemoteException {
- final IDreamOverlayClient client = getClient();
-
- // Inform the overlay service of dream starting.
- client.startDream(mWindowParams, mDreamOverlayCallback,
- LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- // Verify view added.
- verify(mWindowManager).addView(mViewCaptor.capture(), any());
-
- // Service destroyed.
- mService.onDestroy();
- mMainExecutor.runAllReady();
-
- // Verify view removed.
- verify(mWindowManager).removeView(mViewCaptor.getValue());
-
- // Verify state correctly set.
- verify(mKeyguardUpdateMonitor).removeCallback(any());
- verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
- verify(mStateController).setOverlayActive(false);
- verify(mStateController).setLowLightActive(false);
- verify(mStateController).setEntryAnimationsFinished(false);
- }
-
- @Test
- public void testDoNotRemoveViewOnDestroyIfOverlayNotStarted() {
- // Service destroyed without ever starting dream.
- mService.onDestroy();
- mMainExecutor.runAllReady();
-
- // Verify no view is removed.
- verify(mWindowManager, never()).removeView(any());
-
- // Verify state still correctly set.
- verify(mKeyguardUpdateMonitor).removeCallback(any());
- verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
- verify(mStateController).setOverlayActive(false);
- verify(mStateController).setLowLightActive(false);
- }
-
- @Test
- public void testDecorViewNotAddedToWindowAfterDestroy() throws Exception {
- final IDreamOverlayClient client = getClient();
-
- // Destroy the service.
- mService.onDestroy();
- mMainExecutor.runAllReady();
-
- // Inform the overlay service of dream starting.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- verify(mWindowManager, never()).addView(any(), any());
- }
-
- @Test
- public void testNeverRemoveDecorViewIfNotAdded() {
- // Service destroyed before dream started.
- mService.onDestroy();
- mMainExecutor.runAllReady();
-
- verify(mWindowManager, never()).removeView(any());
- }
-
- @Test
- public void testResetCurrentOverlayWhenConnectedToNewDream() throws RemoteException {
- final IDreamOverlayClient client = getClient();
-
- // Inform the overlay service of dream starting. Do not show dream complications.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- // Verify that a new window is added.
- verify(mWindowManager).addView(mViewCaptor.capture(), any());
- final View windowDecorView = mViewCaptor.getValue();
-
- // Assert that the overlay is not showing complications.
- assertThat(mService.shouldShowComplications()).isFalse();
-
- clearInvocations(mDreamOverlayComponent);
- clearInvocations(mWindowManager);
-
- // New dream starting with dream complications showing. Note that when a new dream is
- // binding to the dream overlay service, it receives the same instance of IBinder as the
- // first one.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- true /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- // Assert that the overlay is showing complications.
- assertThat(mService.shouldShowComplications()).isTrue();
-
- // Verify that the old overlay window has been removed, and a new one created.
- verify(mWindowManager).removeView(windowDecorView);
- verify(mWindowManager).addView(any(), any());
-
- // Verify that new instances of overlay container view controller and overlay touch monitor
- // are created.
- verify(mDreamOverlayComponent).getDreamOverlayContainerViewController();
- verify(mDreamOverlayComponent).getDreamOverlayTouchMonitor();
- }
-
- @Test
- public void testWakeUp() throws RemoteException {
- final IDreamOverlayClient client = getClient();
-
- // Inform the overlay service of dream starting.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- true /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- mService.onWakeUp();
- verify(mDreamOverlayContainerViewController).wakeUp();
- verify(mDreamOverlayCallbackController).onWakeUp();
- }
-
- @Test
- public void testWakeUpBeforeStartDoesNothing() {
- mService.onWakeUp();
- verify(mDreamOverlayContainerViewController, never()).wakeUp();
- }
-
- @Test
- public void testSystemFlagShowForAllUsersSetOnWindow() throws RemoteException {
- final IDreamOverlayClient client = getClient();
-
- // Inform the overlay service of dream starting. Do not show dream complications.
- client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
- false /*shouldShowComplication*/);
- mMainExecutor.runAllReady();
-
- final ArgumentCaptor<WindowManager.LayoutParams> paramsCaptor =
- ArgumentCaptor.forClass(WindowManager.LayoutParams.class);
-
- // Verify that a new window is added.
- verify(mWindowManager).addView(any(), paramsCaptor.capture());
-
- assertThat((paramsCaptor.getValue().privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
- == SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isTrue();
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
new file mode 100644
index 0000000..b18a8ec
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams
+
+import android.content.ComponentName
+import android.content.Intent
+import android.os.RemoteException
+import android.service.dreams.IDreamOverlay
+import android.service.dreams.IDreamOverlayCallback
+import android.service.dreams.IDreamOverlayClient
+import android.service.dreams.IDreamOverlayClientCallback
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.view.WindowManagerImpl
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.ambient.touch.TouchMonitor
+import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
+import com.android.systemui.complication.ComplicationHostViewController
+import com.android.systemui.complication.ComplicationLayoutEngine
+import com.android.systemui.complication.dagger.ComplicationComponent
+import com.android.systemui.dreams.complication.HideComplicationTouchHandler
+import com.android.systemui.dreams.dagger.DreamOverlayComponent
+import com.android.systemui.touch.TouchInsetManager
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.invocation.InvocationOnMock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DreamOverlayServiceTest : SysuiTestCase() {
+ private val mFakeSystemClock = FakeSystemClock()
+ private val mMainExecutor = FakeExecutor(mFakeSystemClock)
+
+ @Mock lateinit var mLifecycleOwner: DreamOverlayLifecycleOwner
+
+ @Mock lateinit var mLifecycleRegistry: LifecycleRegistry
+
+ lateinit var mWindowParams: WindowManager.LayoutParams
+
+ @Mock lateinit var mDreamOverlayCallback: IDreamOverlayCallback
+
+ @Mock lateinit var mWindowManager: WindowManagerImpl
+
+ @Mock lateinit var mComplicationComponentFactory: ComplicationComponent.Factory
+
+ @Mock lateinit var mComplicationComponent: ComplicationComponent
+
+ @Mock lateinit var mComplicationHostViewController: ComplicationHostViewController
+
+ @Mock lateinit var mComplicationVisibilityController: ComplicationLayoutEngine
+
+ @Mock
+ lateinit var mDreamComplicationComponentFactory:
+ com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory
+
+ @Mock
+ lateinit var mDreamComplicationComponent:
+ com.android.systemui.dreams.complication.dagger.ComplicationComponent
+
+ @Mock lateinit var mHideComplicationTouchHandler: HideComplicationTouchHandler
+
+ @Mock lateinit var mDreamOverlayComponentFactory: DreamOverlayComponent.Factory
+
+ @Mock lateinit var mDreamOverlayComponent: DreamOverlayComponent
+
+ @Mock lateinit var mAmbientTouchComponentFactory: AmbientTouchComponent.Factory
+
+ @Mock lateinit var mAmbientTouchComponent: AmbientTouchComponent
+
+ @Mock lateinit var mDreamOverlayContainerView: DreamOverlayContainerView
+
+ @Mock lateinit var mDreamOverlayContainerViewController: DreamOverlayContainerViewController
+
+ @Mock lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor
+
+ @Mock lateinit var mTouchMonitor: TouchMonitor
+
+ @Mock lateinit var mStateController: DreamOverlayStateController
+
+ @Mock lateinit var mDreamOverlayContainerViewParent: ViewGroup
+
+ @Mock lateinit var mTouchInsetManager: TouchInsetManager
+
+ @Mock lateinit var mUiEventLogger: UiEventLogger
+
+ @Mock lateinit var mDreamOverlayCallbackController: DreamOverlayCallbackController
+
+ @Captor var mViewCaptor: ArgumentCaptor<View>? = null
+ var mService: DreamOverlayService? = null
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(mDreamOverlayComponent.getDreamOverlayContainerViewController())
+ .thenReturn(mDreamOverlayContainerViewController)
+ whenever(mComplicationComponent.getComplicationHostViewController())
+ .thenReturn(mComplicationHostViewController)
+ whenever(mLifecycleOwner.registry).thenReturn(mLifecycleRegistry)
+ whenever(mComplicationComponentFactory.create(any(), any(), any(), any()))
+ .thenReturn(mComplicationComponent)
+ whenever(mComplicationComponent.getVisibilityController())
+ .thenReturn(mComplicationVisibilityController)
+ whenever(mDreamComplicationComponent.getHideComplicationTouchHandler())
+ .thenReturn(mHideComplicationTouchHandler)
+ whenever(mDreamComplicationComponentFactory.create(any(), any()))
+ .thenReturn(mDreamComplicationComponent)
+ whenever(mDreamOverlayComponentFactory.create(any(), any(), any()))
+ .thenReturn(mDreamOverlayComponent)
+ whenever(mAmbientTouchComponentFactory.create(any(), any()))
+ .thenReturn(mAmbientTouchComponent)
+ whenever(mAmbientTouchComponent.getTouchMonitor()).thenReturn(mTouchMonitor)
+ whenever(mDreamOverlayContainerViewController.containerView)
+ .thenReturn(mDreamOverlayContainerView)
+ mWindowParams = WindowManager.LayoutParams()
+ mService =
+ DreamOverlayService(
+ mContext,
+ mLifecycleOwner,
+ mMainExecutor,
+ mWindowManager,
+ mComplicationComponentFactory,
+ mDreamComplicationComponentFactory,
+ mDreamOverlayComponentFactory,
+ mAmbientTouchComponentFactory,
+ mStateController,
+ mKeyguardUpdateMonitor,
+ mUiEventLogger,
+ mTouchInsetManager,
+ LOW_LIGHT_COMPONENT,
+ HOME_CONTROL_PANEL_DREAM_COMPONENT,
+ mDreamOverlayCallbackController,
+ WINDOW_NAME
+ )
+ }
+
+ @get:Throws(RemoteException::class)
+ val client: IDreamOverlayClient
+ get() {
+ val proxy = mService!!.onBind(Intent())
+ val overlay = IDreamOverlay.Stub.asInterface(proxy)
+ val callback = Mockito.mock(IDreamOverlayClientCallback::class.java)
+ overlay.getClient(callback)
+ val clientCaptor = ArgumentCaptor.forClass(IDreamOverlayClient::class.java)
+ Mockito.verify(callback).onDreamOverlayClient(clientCaptor.capture())
+ return clientCaptor.value
+ }
+
+ @Test
+ fun testOnStartMetricsLogged() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+ Mockito.verify(mUiEventLogger)
+ .log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START)
+ Mockito.verify(mUiEventLogger)
+ .log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START)
+ }
+
+ @Test
+ fun testOverlayContainerViewAddedToWindow() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+ Mockito.verify(mWindowManager).addView(any(), any())
+ }
+
+ // Validates that {@link DreamOverlayService} properly handles the case where the dream's
+ // window is no longer valid by the time start is called.
+ @Test
+ fun testInvalidWindowAddStart() {
+ val client = client
+ Mockito.doThrow(WindowManager.BadTokenException())
+ .`when`(mWindowManager)
+ .addView(any(), any())
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+ Mockito.verify(mWindowManager).addView(any(), any())
+ Mockito.verify(mStateController).setOverlayActive(false)
+ Mockito.verify(mStateController).setLowLightActive(false)
+ Mockito.verify(mStateController).setEntryAnimationsFinished(false)
+ Mockito.verify(mStateController, Mockito.never()).setOverlayActive(true)
+ Mockito.verify(mUiEventLogger, Mockito.never())
+ .log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START)
+ Mockito.verify(mDreamOverlayCallbackController, Mockito.never()).onStartDream()
+ }
+
+ @Test
+ fun testDreamOverlayContainerViewControllerInitialized() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+ Mockito.verify(mDreamOverlayContainerViewController).init()
+ }
+
+ @Test
+ fun testDreamOverlayContainerViewRemovedFromOldParentWhenInitialized() {
+ whenever(mDreamOverlayContainerView.parent)
+ .thenReturn(mDreamOverlayContainerViewParent)
+ .thenReturn(null)
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+ Mockito.verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView)
+ }
+
+ @Test
+ fun testShouldShowComplicationsSetByStartDream() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ true /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+ Truth.assertThat(mService!!.shouldShowComplications()).isTrue()
+ }
+
+ @Test
+ fun testLowLightSetByStartDream() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ LOW_LIGHT_COMPONENT.flattenToString(),
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+ Truth.assertThat(mService!!.dreamComponent).isEqualTo(LOW_LIGHT_COMPONENT)
+ Mockito.verify(mStateController).setLowLightActive(true)
+ }
+
+ @Test
+ fun testHomeControlPanelSetsByStartDream() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ HOME_CONTROL_PANEL_DREAM_COMPONENT.flattenToString(),
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+ Truth.assertThat(mService!!.dreamComponent).isEqualTo(HOME_CONTROL_PANEL_DREAM_COMPONENT)
+ Mockito.verify(mStateController).setHomeControlPanelActive(true)
+ }
+
+ @Test
+ fun testOnEndDream() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ LOW_LIGHT_COMPONENT.flattenToString(),
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+
+ // Verify view added.
+ Mockito.verify(mWindowManager).addView(mViewCaptor!!.capture(), any())
+
+ // Service destroyed.
+ mService!!.onEndDream()
+ mMainExecutor.runAllReady()
+
+ // Verify view removed.
+ Mockito.verify(mWindowManager).removeView(mViewCaptor!!.value)
+
+ // Verify state correctly set.
+ Mockito.verify(mStateController).setOverlayActive(false)
+ Mockito.verify(mStateController).setLowLightActive(false)
+ Mockito.verify(mStateController).setEntryAnimationsFinished(false)
+ }
+
+ @Test
+ fun testImmediateEndDream() {
+ val client = client
+
+ // Start the dream, but don't execute any Runnables put on the executor yet. We delay
+ // executing Runnables as the timing isn't guaranteed and we want to verify that the overlay
+ // starts and finishes in the proper order even if Runnables are delayed.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*shouldShowComplication*/
+ )
+ // Immediately end the dream.
+ client.endDream()
+ // Run any scheduled Runnables.
+ mMainExecutor.runAllReady()
+
+ // The overlay starts then finishes.
+ val inOrder = Mockito.inOrder(mWindowManager)
+ inOrder.verify(mWindowManager).addView(mViewCaptor!!.capture(), any())
+ inOrder.verify(mWindowManager).removeView(mViewCaptor!!.value)
+ }
+
+ @Test
+ fun testEndDreamDuringStartDream() {
+ val client = client
+
+ // Schedule the endDream call in the middle of the startDream implementation, as any
+ // ordering is possible.
+ Mockito.doAnswer { invocation: InvocationOnMock? ->
+ client.endDream()
+ null
+ }
+ .`when`(mStateController)
+ .setOverlayActive(true)
+
+ // Start the dream.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+
+ // The overlay starts then finishes.
+ val inOrder = Mockito.inOrder(mWindowManager)
+ inOrder.verify(mWindowManager).addView(mViewCaptor!!.capture(), any())
+ inOrder.verify(mWindowManager).removeView(mViewCaptor!!.value)
+ }
+
+ @Test
+ fun testDestroy() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ LOW_LIGHT_COMPONENT.flattenToString(),
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+
+ // Verify view added.
+ Mockito.verify(mWindowManager).addView(mViewCaptor!!.capture(), any())
+
+ // Service destroyed.
+ mService!!.onDestroy()
+ mMainExecutor.runAllReady()
+
+ // Verify view removed.
+ Mockito.verify(mWindowManager).removeView(mViewCaptor!!.value)
+
+ // Verify state correctly set.
+ Mockito.verify(mKeyguardUpdateMonitor).removeCallback(any())
+ Mockito.verify(mLifecycleRegistry).currentState = Lifecycle.State.DESTROYED
+ Mockito.verify(mStateController).setOverlayActive(false)
+ Mockito.verify(mStateController).setLowLightActive(false)
+ Mockito.verify(mStateController).setEntryAnimationsFinished(false)
+ }
+
+ @Test
+ fun testDoNotRemoveViewOnDestroyIfOverlayNotStarted() {
+ // Service destroyed without ever starting dream.
+ mService!!.onDestroy()
+ mMainExecutor.runAllReady()
+
+ // Verify no view is removed.
+ Mockito.verify(mWindowManager, Mockito.never()).removeView(any())
+
+ // Verify state still correctly set.
+ Mockito.verify(mKeyguardUpdateMonitor).removeCallback(any())
+ Mockito.verify(mLifecycleRegistry).currentState = Lifecycle.State.DESTROYED
+ Mockito.verify(mStateController).setOverlayActive(false)
+ Mockito.verify(mStateController).setLowLightActive(false)
+ }
+
+ @Test
+ fun testDecorViewNotAddedToWindowAfterDestroy() {
+ val client = client
+
+ // Destroy the service.
+ mService!!.onDestroy()
+ mMainExecutor.runAllReady()
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+ Mockito.verify(mWindowManager, Mockito.never()).addView(any(), any())
+ }
+
+ @Test
+ fun testNeverRemoveDecorViewIfNotAdded() {
+ // Service destroyed before dream started.
+ mService!!.onDestroy()
+ mMainExecutor.runAllReady()
+ Mockito.verify(mWindowManager, Mockito.never()).removeView(any())
+ }
+
+ @Test
+ fun testResetCurrentOverlayWhenConnectedToNewDream() {
+ val client = client
+
+ // Inform the overlay service of dream starting. Do not show dream complications.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+
+ // Verify that a new window is added.
+ Mockito.verify(mWindowManager).addView(mViewCaptor!!.capture(), any())
+ val windowDecorView = mViewCaptor!!.value
+
+ // Assert that the overlay is not showing complications.
+ Truth.assertThat(mService!!.shouldShowComplications()).isFalse()
+ Mockito.clearInvocations(mDreamOverlayComponent)
+ Mockito.clearInvocations(mAmbientTouchComponent)
+ Mockito.clearInvocations(mWindowManager)
+
+ // New dream starting with dream complications showing. Note that when a new dream is
+ // binding to the dream overlay service, it receives the same instance of IBinder as the
+ // first one.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ true /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+
+ // Assert that the overlay is showing complications.
+ Truth.assertThat(mService!!.shouldShowComplications()).isTrue()
+
+ // Verify that the old overlay window has been removed, and a new one created.
+ Mockito.verify(mWindowManager).removeView(windowDecorView)
+ Mockito.verify(mWindowManager).addView(any(), any())
+
+ // Verify that new instances of overlay container view controller and overlay touch monitor
+ // are created.
+ Mockito.verify(mDreamOverlayComponent).getDreamOverlayContainerViewController()
+ Mockito.verify(mAmbientTouchComponent).getTouchMonitor()
+ }
+
+ @Test
+ fun testWakeUp() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ true /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+ mService!!.onWakeUp()
+ Mockito.verify(mDreamOverlayContainerViewController).wakeUp()
+ Mockito.verify(mDreamOverlayCallbackController).onWakeUp()
+ }
+
+ @Test
+ fun testWakeUpBeforeStartDoesNothing() {
+ mService!!.onWakeUp()
+ Mockito.verify(mDreamOverlayContainerViewController, Mockito.never()).wakeUp()
+ }
+
+ @Test
+ fun testSystemFlagShowForAllUsersSetOnWindow() {
+ val client = client
+
+ // Inform the overlay service of dream starting. Do not show dream complications.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+ val paramsCaptor = ArgumentCaptor.forClass(WindowManager.LayoutParams::class.java)
+
+ // Verify that a new window is added.
+ Mockito.verify(mWindowManager).addView(any(), paramsCaptor.capture())
+ Truth.assertThat(
+ paramsCaptor.value.privateFlags and
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS ==
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+ )
+ .isTrue()
+ }
+
+ companion object {
+ private val LOW_LIGHT_COMPONENT = ComponentName("package", "lowlight")
+ private val HOME_CONTROL_PANEL_DREAM_COMPONENT =
+ ComponentName("package", "homeControlPanel")
+ private const val DREAM_COMPONENT = "package/dream"
+ private const val WINDOW_NAME = "test"
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 39db2be..f561c53 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -36,11 +36,8 @@
import android.content.Context;
import android.content.res.Resources;
import android.hardware.SensorPrivacyManager;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
import android.provider.Settings;
+import android.testing.TestableLooper;
import android.view.View;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -51,6 +48,7 @@
import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -72,6 +70,7 @@
import java.util.concurrent.Executor;
@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4.class)
public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
private static final String NOTIFICATION_INDICATOR_FORMATTER_STRING =
@@ -80,12 +79,6 @@
@Mock
MockDreamOverlayStatusBarView mView;
@Mock
- ConnectivityManager mConnectivityManager;
- @Mock
- NetworkCapabilities mNetworkCapabilities;
- @Mock
- Network mNetwork;
- @Mock
TouchInsetManager.TouchInsetSession mTouchSession;
@Mock
Resources mResources;
@@ -121,6 +114,8 @@
private final Executor mMainExecutor = Runnable::run;
+ private final FakeWifiRepository mWifiRepository = new FakeWifiRepository();
+
DreamOverlayStatusBarViewController mController;
@Before
@@ -137,7 +132,6 @@
mView,
mResources,
mMainExecutor,
- mConnectivityManager,
mTouchSession,
mAlarmManager,
mNextAlarmController,
@@ -149,6 +143,7 @@
mDreamOverlayStatusBarItemsProvider,
mDreamOverlayStateController,
mUserTracker,
+ mWifiRepository,
mLogBuffer);
}
@@ -164,42 +159,24 @@
}
@Test
- public void testOnViewAttachedRegistersNetworkCallback() {
+ public void testWifiIconShownWhenWifiUnavailable() {
mController.onViewAttached();
- verify(mConnectivityManager)
- .registerNetworkCallback(any(NetworkRequest.class), any(
- ConnectivityManager.NetworkCallback.class));
- }
+ mController.updateWifiUnavailableStatusIcon(false);
- @Test
- public void testOnViewAttachedShowsWifiIconWhenWifiUnavailable() {
- when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
- .thenReturn(false);
- when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
- mController.onViewAttached();
verify(mView).showIcon(
DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null);
}
@Test
- public void testOnViewAttachedHidesWifiIconWhenWifiAvailable() {
- when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
- .thenReturn(true);
- when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ public void testWifiIconHiddenWhenWifiAvailable() {
mController.onViewAttached();
+ mController.updateWifiUnavailableStatusIcon(true);
+
verify(mView).showIcon(
DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null);
}
@Test
- public void testOnViewAttachedShowsWifiIconWhenNetworkCapabilitiesUnavailable() {
- when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(null);
- mController.onViewAttached();
- verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null);
- }
-
- @Test
public void testOnViewAttachedShowsAlarmIconWhenAlarmExists() {
final AlarmManager.AlarmClockInfo alarmClockInfo =
new AlarmManager.AlarmClockInfo(1L, null);
@@ -282,7 +259,6 @@
mView,
mResources,
mMainExecutor,
- mConnectivityManager,
mTouchSession,
mAlarmManager,
mNextAlarmController,
@@ -294,6 +270,7 @@
mDreamOverlayStatusBarItemsProvider,
mDreamOverlayStateController,
mUserTracker,
+ mWifiRepository,
mLogBuffer);
controller.onViewAttached();
verify(mView, never()).showIcon(
@@ -319,13 +296,6 @@
}
@Test
- public void testOnViewDetachedUnregistersNetworkCallback() {
- mController.onViewDetached();
- verify(mConnectivityManager)
- .unregisterNetworkCallback(any(ConnectivityManager.NetworkCallback.class));
- }
-
- @Test
public void testOnViewDetachedRemovesCallbacks() {
mController.onViewDetached();
verify(mNextAlarmController).removeCallback(any());
@@ -343,61 +313,6 @@
}
@Test
- public void testWifiIconHiddenWhenWifiBecomesAvailable() {
- // Make sure wifi starts out unavailable when onViewAttached is called, and then returns
- // true on the second query.
- when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
- .thenReturn(false).thenReturn(true);
- when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
- mController.onViewAttached();
-
- final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture =
- ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
- verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
- callbackCapture.getValue().onAvailable(mNetwork);
-
- verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null);
- }
-
- @Test
- public void testWifiIconShownWhenWifiBecomesUnavailable() {
- // Make sure wifi starts out available when onViewAttached is called, then returns false
- // on the second query.
- when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
- .thenReturn(true).thenReturn(false);
- when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
- mController.onViewAttached();
-
- final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture =
- ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
- verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
- callbackCapture.getValue().onLost(mNetwork);
-
- verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null);
- }
-
- @Test
- public void testWifiIconHiddenWhenCapabilitiesChange() {
- // Make sure wifi starts out unavailable when onViewAttached is called.
- when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
- .thenReturn(false);
- when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
- mController.onViewAttached();
-
- final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture =
- ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
- verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
- when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
- .thenReturn(true);
- callbackCapture.getValue().onCapabilitiesChanged(mNetwork, mNetworkCapabilities);
-
- verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null);
- }
-
- @Test
public void testNotificationsIconShownWhenNotificationAdded() {
mController.onViewAttached();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
index 315a24b..8481586 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.dreams.complication;
+import static com.android.systemui.Flags.FLAG_REMOVE_DREAM_OVERLAY_HIDE_ON_TOUCH;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
@@ -23,6 +25,8 @@
import static org.mockito.Mockito.when;
import android.os.Handler;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.view.MotionEvent;
import android.view.View;
@@ -31,9 +35,9 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.ambient.touch.TouchHandler;
import com.android.systemui.complication.Complication;
import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.dreams.touch.DreamTouchHandler;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.touch.TouchInsetManager;
@@ -70,7 +74,7 @@
MotionEvent mMotionEvent;
@Mock
- DreamTouchHandler.TouchSession mSession;
+ TouchHandler.TouchSession mSession;
@Mock
DreamOverlayStateController mStateController;
@@ -90,6 +94,7 @@
* Ensures no actions are taken when there multiple sessions.
*/
@Test
+ @DisableFlags(FLAG_REMOVE_DREAM_OVERLAY_HIDE_ON_TOUCH)
public void testSessionEndOnMultipleSessions() {
final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler(
mVisibilityController,
@@ -122,6 +127,7 @@
* Ensures no actions are taken when the bouncer is showing.
*/
@Test
+ @DisableFlags(FLAG_REMOVE_DREAM_OVERLAY_HIDE_ON_TOUCH)
public void testSessionEndWhenBouncerShowing() {
final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler(
mVisibilityController,
@@ -154,6 +160,7 @@
* Ensures no actions are taken when there multiple sessions.
*/
@Test
+ @DisableFlags(FLAG_REMOVE_DREAM_OVERLAY_HIDE_ON_TOUCH)
public void testSessionEndWithTouchInInset() {
final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler(
mVisibilityController,
@@ -202,6 +209,7 @@
* Make sure visibility changes are triggered from session events.
*/
@Test
+ @DisableFlags(FLAG_REMOVE_DREAM_OVERLAY_HIDE_ON_TOUCH)
public void testSessionLifecycle() {
final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler(
mVisibilityController,
@@ -259,4 +267,34 @@
// Verify session ended.
verify(mSession).pop();
}
+
+ @Test
+ @EnableFlags(FLAG_REMOVE_DREAM_OVERLAY_HIDE_ON_TOUCH)
+ public void testNoActionWhenDisabledByFlag() {
+ final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler(
+ mVisibilityController,
+ RESTORE_TIMEOUT,
+ HIDE_DELAY,
+ mTouchInsetManager,
+ mStatusBarKeyguardViewManager,
+ mFakeExecutor,
+ mStateController);
+
+ // Report one session.
+ when(mSession.getActiveSessionCount()).thenReturn(1);
+
+ // Bouncer is showing.
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+
+ // Start session.
+ touchHandler.onSessionStart(mSession);
+
+ // Verify session end.
+ verify(mSession).pop();
+
+ mClock.advanceTime(HIDE_DELAY);
+
+ // Verify no interaction with visibility controller.
+ verify(mVisibilityController, never()).setVisibility(anyInt());
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index 8dcf903..29fbee0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -31,6 +31,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.ambient.touch.TouchHandler;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -54,7 +55,7 @@
@Mock
CentralSurfaces mCentralSurfaces;
@Mock
- DreamTouchHandler.TouchSession mTouchSession;
+ TouchHandler.TouchSession mTouchSession;
CommunalTouchHandler mTouchHandler;
@Mock
Lifecycle mLifecycle;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index 8f03717..3889703 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -26,39 +26,34 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.haptics.vibratorHelper
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
@SmallTest
@RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalCoroutinesApi::class)
@RunWithLooper(setAsMainLooper = true)
class QSLongPressEffectTest : SysuiTestCase() {
@Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule()
- @Mock private lateinit var vibratorHelper: VibratorHelper
@Mock private lateinit var testView: View
@get:Rule val animatorTestRule = AnimatorTestRule(this)
private val kosmos = testKosmos()
+ private val vibratorHelper = kosmos.vibratorHelper
private val effectDuration = 400
private val lowTickDuration = 12
@@ -68,19 +63,71 @@
@Before
fun setup() {
- whenever(
- vibratorHelper.getPrimitiveDurations(
- VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
- VibrationEffect.Composition.PRIMITIVE_SPIN,
- )
- )
- .thenReturn(intArrayOf(lowTickDuration, spinDuration))
+ vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_LOW_TICK] =
+ lowTickDuration
+ vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_SPIN] = spinDuration
+
+ kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
longPressEffect =
QSLongPressEffect(
vibratorHelper,
- effectDuration,
+ kosmos.keyguardInteractor,
+ CoroutineScope(kosmos.backgroundCoroutineContext),
)
+ longPressEffect.initializeEffect(effectDuration)
+ }
+
+ @Test
+ fun onReset_whileIdle_resetsEffect() = testWithScope {
+ // GIVEN a call to reset
+ longPressEffect.resetEffect()
+
+ // THEN the effect remains idle and has not been initialized
+ val state by collectLastValue(longPressEffect.state)
+ assertThat(state).isEqualTo(QSLongPressEffect.State.IDLE)
+ assertThat(longPressEffect.hasInitialized).isFalse()
+ }
+
+ @Test
+ fun onReset_whileRunning_resetsEffect() = testWhileRunning {
+ // GIVEN a call to reset
+ longPressEffect.resetEffect()
+
+ // THEN the effect remains idle and has not been initialized
+ val state by collectLastValue(longPressEffect.state)
+ assertThat(state).isEqualTo(QSLongPressEffect.State.IDLE)
+ assertThat(longPressEffect.hasInitialized).isFalse()
+ }
+
+ @Test
+ fun onInitialize_withNegativeDuration_doesNotInitialize() = testWithScope {
+ // GIVEN an effect that has reset
+ longPressEffect.resetEffect()
+
+ // WHEN attempting to initialize with a negative duration
+ val couldInitialize = longPressEffect.initializeEffect(-1)
+
+ // THEN the effect can't initialized and remains reset
+ val state by collectLastValue(longPressEffect.state)
+ assertThat(couldInitialize).isFalse()
+ assertThat(state).isEqualTo(QSLongPressEffect.State.IDLE)
+ assertThat(longPressEffect.hasInitialized).isFalse()
+ }
+
+ @Test
+ fun onInitialize_withPositiveDuration_initializes() = testWithScope {
+ // GIVEN an effect that has reset
+ longPressEffect.resetEffect()
+
+ // WHEN attempting to initialize with a positive duration
+ val couldInitialize = longPressEffect.initializeEffect(effectDuration)
+
+ // THEN the effect is initialized
+ val state by collectLastValue(longPressEffect.state)
+ assertThat(couldInitialize).isTrue()
+ assertThat(state).isEqualTo(QSLongPressEffect.State.IDLE)
+ assertThat(longPressEffect.hasInitialized).isTrue()
}
@Test
@@ -90,7 +137,8 @@
longPressEffect.onTouch(testView, downEvent)
// THEN the effect moves to the TIMEOUT_WAIT state
- assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.TIMEOUT_WAIT)
+ val state by collectLastValue(longPressEffect.state)
+ assertThat(state).isEqualTo(QSLongPressEffect.State.TIMEOUT_WAIT)
}
@Test
@@ -100,7 +148,8 @@
longPressEffect.onTouch(testView, cancelEvent)
// THEN the effect goes back to idle and does not start
- assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
+ val state by collectLastValue(longPressEffect.state)
+ assertThat(state).isEqualTo(QSLongPressEffect.State.IDLE)
assertEffectDidNotStart()
}
@@ -121,7 +170,7 @@
@Test
fun onWaitComplete_whileWaiting_beginsEffect() = testWhileWaiting {
// GIVEN the pressed timeout is complete
- advanceTimeBy(QSLongPressEffect.PRESSED_TIMEOUT + 10L)
+ longPressEffect.handleTimeoutComplete()
// THEN the effect starts
assertEffectStarted()
@@ -154,15 +203,28 @@
}
@Test
- fun onAnimationComplete_effectEnds() = testWhileRunning {
+ fun onAnimationComplete_keyguardDismissible_effectEndsWithLongPress() = testWhileRunning {
// GIVEN that the animation completes
animatorTestRule.advanceTimeBy(effectDuration + 10L)
- // THEN the long-press effect completes
- assertEffectCompleted()
+ // THEN the long-press effect completes with a LONG_PRESS
+ assertEffectCompleted(QSLongPressEffect.ActionType.LONG_PRESS)
}
@Test
+ fun onAnimationComplete_keyguardNotDismissible_effectEndsWithResetAndLongPress() =
+ testWhileRunning {
+ // GIVEN that the keyguard is not dismissible
+ kosmos.fakeKeyguardRepository.setKeyguardDismissible(false)
+
+ // GIVEN that the animation completes
+ animatorTestRule.advanceTimeBy(effectDuration + 10L)
+
+ // THEN the long-press effect completes with RESET_AND_LONG_PRESS
+ assertEffectCompleted(QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS)
+ }
+
+ @Test
fun onActionDown_whileRunningBackwards_resets() = testWhileRunning {
// GIVEN that the effect is at the middle of its completion (progress of 50%)
animatorTestRule.advanceTimeBy(effectDuration / 2L)
@@ -192,33 +254,21 @@
animatorTestRule.advanceTimeBy(effectDuration.toLong())
// THEN the state goes to [QSLongPressEffect.State.IDLE]
- assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
+ val state by collectLastValue(longPressEffect.state)
+ assertThat(state).isEqualTo(QSLongPressEffect.State.IDLE)
}
private fun buildMotionEvent(action: Int): MotionEvent =
MotionEventBuilder.newBuilder().setAction(action).build()
private fun testWithScope(test: suspend TestScope.() -> Unit) =
- with(kosmos) {
- testScope.runTest {
- // GIVEN an effect with a testing scope
- longPressEffect.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
-
- // THEN run the test
- test()
- }
- }
+ with(kosmos) { testScope.runTest { test() } }
private fun testWhileWaiting(test: suspend TestScope.() -> Unit) =
with(kosmos) {
testScope.runTest {
- // GIVEN an effect with a testing scope
- longPressEffect.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
-
// GIVEN the TIMEOUT_WAIT state is entered
- val downEvent =
- MotionEventBuilder.newBuilder().setAction(MotionEvent.ACTION_DOWN).build()
- longPressEffect.onTouch(testView, downEvent)
+ longPressEffect.setState(QSLongPressEffect.State.TIMEOUT_WAIT)
// THEN run the test
test()
@@ -228,16 +278,9 @@
private fun testWhileRunning(test: suspend TestScope.() -> Unit) =
with(kosmos) {
testScope.runTest {
- // GIVEN an effect with a testing scope
- longPressEffect.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
-
- // GIVEN the down event that enters the TIMEOUT_WAIT state
- val downEvent =
- MotionEventBuilder.newBuilder().setAction(MotionEvent.ACTION_DOWN).build()
- longPressEffect.onTouch(testView, downEvent)
-
- // GIVEN that the timeout completes and the effect starts
- advanceTimeBy(QSLongPressEffect.PRESSED_TIMEOUT + 10L)
+ // GIVEN that the effect starts after the tap timeout is complete
+ longPressEffect.setState(QSLongPressEffect.State.TIMEOUT_WAIT)
+ longPressEffect.handleTimeoutComplete()
// THEN run the test
test()
@@ -252,6 +295,7 @@
*/
private fun TestScope.assertEffectStarted() {
val effectProgress by collectLastValue(longPressEffect.effectProgress)
+ val state by collectLastValue(longPressEffect.state)
val longPressHint =
LongPressHapticBuilder.createLongPressHint(
lowTickDuration,
@@ -259,10 +303,10 @@
effectDuration,
)
- assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.RUNNING_FORWARD)
+ assertThat(state).isEqualTo(QSLongPressEffect.State.RUNNING_FORWARD)
assertThat(effectProgress).isEqualTo(0f)
assertThat(longPressHint).isNotNull()
- verify(vibratorHelper).vibrate(longPressHint!!)
+ assertThat(vibratorHelper.hasVibratedWithEffects(longPressHint!!)).isTrue()
}
/**
@@ -274,11 +318,12 @@
*/
private fun TestScope.assertEffectDidNotStart() {
val effectProgress by collectLastValue(longPressEffect.effectProgress)
+ val state by collectLastValue(longPressEffect.state)
- assertThat(longPressEffect.state).isNotEqualTo(QSLongPressEffect.State.RUNNING_FORWARD)
- assertThat(longPressEffect.state).isNotEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS)
+ assertThat(state).isNotEqualTo(QSLongPressEffect.State.RUNNING_FORWARD)
+ assertThat(state).isNotEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS)
assertThat(effectProgress).isNull()
- verify(vibratorHelper, never()).vibrate(any(/* type= */ VibrationEffect::class.java))
+ assertThat(vibratorHelper.totalVibrations).isEqualTo(0)
}
/**
@@ -286,18 +331,19 @@
* 1. The progress is null
* 2. The final snap haptics are played
* 3. The internal state goes back to [QSLongPressEffect.State.IDLE]
- * 4. The action to perform on the tile is the long-press action
+ * 4. The action to perform on the tile is the action given as a parameter
*/
- private fun TestScope.assertEffectCompleted() {
+ private fun TestScope.assertEffectCompleted(expectedAction: QSLongPressEffect.ActionType) {
val action by collectLastValue(longPressEffect.actionType)
val effectProgress by collectLastValue(longPressEffect.effectProgress)
val snapEffect = LongPressHapticBuilder.createSnapEffect()
+ val state by collectLastValue(longPressEffect.state)
assertThat(effectProgress).isNull()
assertThat(snapEffect).isNotNull()
- verify(vibratorHelper).vibrate(snapEffect!!)
- assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
- assertThat(action).isEqualTo(QSLongPressEffect.ActionType.LONG_PRESS)
+ assertThat(vibratorHelper.hasVibratedWithEffects(snapEffect!!)).isTrue()
+ assertThat(state).isEqualTo(QSLongPressEffect.State.IDLE)
+ assertThat(action).isEqualTo(expectedAction)
}
/**
@@ -305,17 +351,18 @@
* 1. The internal state is [QSLongPressEffect.State.RUNNING_BACKWARDS]
* 2. The reverse haptics plays at the point where the animation was paused
*/
- private fun assertEffectReverses(pausedProgress: Float) {
+ private fun TestScope.assertEffectReverses(pausedProgress: Float) {
val reverseHaptics =
LongPressHapticBuilder.createReversedEffect(
pausedProgress,
lowTickDuration,
effectDuration,
)
+ val state by collectLastValue(longPressEffect.state)
- assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS)
+ assertThat(state).isEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS)
assertThat(reverseHaptics).isNotNull()
- verify(vibratorHelper).vibrate(reverseHaptics!!)
+ assertThat(vibratorHelper.hasVibratedWithEffects(reverseHaptics!!)).isTrue()
}
/**
@@ -325,8 +372,9 @@
*/
private fun TestScope.assertEffectResets() {
val effectProgress by collectLastValue(longPressEffect.effectProgress)
- assertThat(effectProgress).isEqualTo(0f)
+ val state by collectLastValue(longPressEffect.state)
- assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.TIMEOUT_WAIT)
+ assertThat(effectProgress).isNull()
+ assertThat(state).isEqualTo(QSLongPressEffect.State.TIMEOUT_WAIT)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt
new file mode 100644
index 0000000..c88e432
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardClockSwitch.LARGE
+import com.android.keyguard.KeyguardClockSwitch.SMALL
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardClockRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.data.repository.mediaFilterRepository
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
+import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
+import com.android.systemui.testKosmos
+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
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardClockInteractorTest : SysuiTestCase() {
+ private lateinit var kosmos: Kosmos
+ private lateinit var underTest: KeyguardClockInteractor
+ private lateinit var testScope: TestScope
+
+ @Before
+ fun setup() {
+ kosmos = testKosmos()
+ testScope = kosmos.testScope
+ underTest = kosmos.keyguardClockInteractor
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun clockSize_sceneContainerFlagOff_basedOnRepository() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockSize)
+ kosmos.keyguardClockRepository.setClockSize(LARGE)
+ assertThat(value).isEqualTo(LARGE)
+
+ kosmos.keyguardClockRepository.setClockSize(SMALL)
+ assertThat(value).isEqualTo(SMALL)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun clockShouldBeCentered_sceneContainerFlagOff_basedOnRepository() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockShouldBeCentered)
+ kosmos.keyguardInteractor.setClockShouldBeCentered(true)
+ assertThat(value).isEqualTo(true)
+
+ kosmos.keyguardInteractor.setClockShouldBeCentered(false)
+ assertThat(value).isEqualTo(false)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun clockSize_forceSmallClock_SMALL() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockSize)
+ kosmos.fakeKeyguardClockRepository.setShouldForceSmallClock(true)
+ kosmos.fakeFeatureFlagsClassic.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, true)
+ transitionTo(KeyguardState.AOD, KeyguardState.LOCKSCREEN)
+ assertThat(value).isEqualTo(SMALL)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun clockSize_SceneContainerFlagOn_shadeModeSingle_hasNotifs_SMALL() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockSize)
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Single)
+ kosmos.activeNotificationListRepository.setActiveNotifs(1)
+ assertThat(value).isEqualTo(SMALL)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun clockSize_SceneContainerFlagOn_shadeModeSingle_hasMedia_SMALL() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockSize)
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Single)
+ val userMedia = MediaData().copy(active = true)
+ kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ assertThat(value).isEqualTo(SMALL)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun clockSize_SceneContainerFlagOn_shadeModeSplit_isMediaVisible_SMALL() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockSize)
+ val userMedia = MediaData().copy(active = true)
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
+ kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ kosmos.keyguardRepository.setIsDozing(false)
+ assertThat(value).isEqualTo(SMALL)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun clockSize_SceneContainerFlagOn_shadeModeSplit_noMedia_LARGE() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockSize)
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
+ kosmos.keyguardRepository.setIsDozing(false)
+ assertThat(value).isEqualTo(LARGE)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun clockSize_SceneContainerFlagOn_shadeModeSplit_isDozing_LARGE() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockSize)
+ val userMedia = MediaData().copy(active = true)
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
+ kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ kosmos.keyguardRepository.setIsDozing(true)
+ assertThat(value).isEqualTo(LARGE)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun clockShouldBeCentered_sceneContainerFlagOn_notSplitMode_true() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockShouldBeCentered)
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Single)
+ assertThat(value).isEqualTo(true)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_noActiveNotifications_true() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockShouldBeCentered)
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
+ kosmos.activeNotificationListRepository.setActiveNotifs(0)
+ assertThat(value).isEqualTo(true)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_isActiveDreamLockscreenHosted_true() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockShouldBeCentered)
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
+ kosmos.activeNotificationListRepository.setActiveNotifs(1)
+ kosmos.keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+ assertThat(value).isEqualTo(true)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_hasPulsingNotifications_false() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockShouldBeCentered)
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
+ kosmos.activeNotificationListRepository.setActiveNotifs(1)
+ kosmos.headsUpNotificationRepository.isHeadsUpAnimatingAway.value = true
+ kosmos.keyguardRepository.setIsDozing(true)
+ assertThat(value).isEqualTo(false)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_onAod_true() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockShouldBeCentered)
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
+ kosmos.activeNotificationListRepository.setActiveNotifs(1)
+ transitionTo(KeyguardState.LOCKSCREEN, KeyguardState.AOD)
+ assertThat(value).isEqualTo(true)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_offAod_false() =
+ testScope.runTest {
+ val value by collectLastValue(underTest.clockShouldBeCentered)
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
+ kosmos.activeNotificationListRepository.setActiveNotifs(1)
+ transitionTo(KeyguardState.AOD, KeyguardState.LOCKSCREEN)
+ assertThat(value).isEqualTo(false)
+ }
+
+ private suspend fun transitionTo(from: KeyguardState, to: KeyguardState) {
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from, to, 0f, TransitionState.STARTED)
+ )
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from, to, 0.5f, TransitionState.RUNNING)
+ )
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from, to, 1f, TransitionState.FINISHED)
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 6d7a0a9..12f8918 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -37,7 +38,6 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
@@ -75,7 +75,6 @@
repository = repository,
commandQueue = commandQueue,
powerInteractor = PowerInteractorFactory.create().powerInteractor,
- sceneContainerFlags = kosmos.fakeSceneContainerFlags,
bouncerRepository = bouncerRepository,
configurationInteractor = ConfigurationInteractor(FakeConfigurationRepository()),
shadeRepository = shadeRepository,
@@ -248,9 +247,9 @@
}
@Test
+ @EnableSceneContainer
fun animationDozingTransitions() =
testScope.runTest {
- kosmos.fakeSceneContainerFlags.enabled = true
val isAnimate by collectLastValue(underTest.animateDozingTransitions)
underTest.setAnimateDozingTransitions(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 15c9cf7..41229255 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -55,29 +55,30 @@
val testScope = kosmos.testScope
@Test
- fun transitionCollectorsReceivesOnlyAppropriateEvents() = runTest {
- val lockscreenToAodSteps by collectValues(underTest.lockscreenToAodTransition)
- val aodToLockscreenSteps by collectValues(underTest.aodToLockscreenTransition)
+ fun transitionCollectorsReceivesOnlyAppropriateEvents() =
+ testScope.runTest {
+ val lockscreenToAodSteps by collectValues(underTest.transition(LOCKSCREEN, AOD))
+ val aodToLockscreenSteps by collectValues(underTest.transition(AOD, LOCKSCREEN))
- val steps = mutableListOf<TransitionStep>()
- steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
- steps.add(TransitionStep(AOD, GONE, 1f, FINISHED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.1f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.2f, RUNNING))
+ val steps = mutableListOf<TransitionStep>()
+ steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
+ steps.add(TransitionStep(AOD, GONE, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.1f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.2f, RUNNING))
- steps.forEach {
- repository.sendTransitionStep(it)
- runCurrent()
+ steps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
+ }
+
+ assertThat(aodToLockscreenSteps).isEqualTo(steps.subList(2, 5))
+ assertThat(lockscreenToAodSteps).isEqualTo(steps.subList(5, 8))
}
- assertThat(aodToLockscreenSteps).isEqualTo(steps.subList(2, 5))
- assertThat(lockscreenToAodSteps).isEqualTo(steps.subList(5, 8))
- }
-
@Test
fun dozeAmountTransitionTest_AodToFromLockscreen() =
testScope.runTest {
@@ -187,59 +188,60 @@
}
@Test
- fun finishedKeyguardTransitionStepTests() = runTest {
- val finishedSteps by collectValues(underTest.finishedKeyguardTransitionStep)
+ fun finishedKeyguardTransitionStepTests() =
+ testScope.runTest {
+ val finishedSteps by collectValues(underTest.finishedKeyguardTransitionStep)
+ val steps = mutableListOf<TransitionStep>()
- val steps = mutableListOf<TransitionStep>()
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
- steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
+ steps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
+ }
- steps.forEach {
- repository.sendTransitionStep(it)
- runCurrent()
+ // Ignore the default state.
+ assertThat(finishedSteps.subList(1, finishedSteps.size))
+ .isEqualTo(listOf(steps[2], steps[5]))
}
- // Ignore the default state.
- assertThat(finishedSteps.subList(1, finishedSteps.size))
- .isEqualTo(listOf(steps[2], steps[5]))
- }
-
@Test
- fun startedKeyguardTransitionStepTests() = runTest {
- val startedSteps by collectValues(underTest.startedKeyguardTransitionStep)
+ fun startedKeyguardTransitionStepTests() =
+ testScope.runTest {
+ val startedSteps by collectValues(underTest.startedKeyguardTransitionStep)
- val steps = mutableListOf<TransitionStep>()
+ val steps = mutableListOf<TransitionStep>()
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
- steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
- steps.forEach {
- repository.sendTransitionStep(it)
- runCurrent()
- }
+ steps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
+ }
- assertThat(startedSteps)
- .isEqualTo(
- listOf(
- // The initial transition will also get sent when collect started
- TransitionStep(OFF, LOCKSCREEN, 0f, STARTED),
- steps[0],
- steps[3],
- steps[6]
+ assertThat(startedSteps)
+ .isEqualTo(
+ listOf(
+ // The initial transition will also get sent when collect started
+ TransitionStep(OFF, LOCKSCREEN, 0f, STARTED),
+ steps[0],
+ steps[3],
+ steps[6]
+ )
)
- )
- }
+ }
@Test
fun transitionValue() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
index f0607f4..0ac7ff5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.SysuiTestCase
@@ -35,10 +36,9 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class KeyguardTransitionAnimationFlowTest : SysuiTestCase() {
val kosmos = testKosmos()
val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelTest.kt
new file mode 100644
index 0000000..d0f434a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AccessibilityActionsViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val keyguardRepository = kosmos.fakeKeyguardRepository
+ private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+
+ private lateinit var underTest: AccessibilityActionsViewModel
+
+ @Before
+ fun setUp() {
+ underTest = kosmos.accessibilityActionsViewModelKosmos
+ }
+
+ @Test
+ fun isOnKeyguard_isFalse_whenTransitioningAwayFromLockscreen() =
+ testScope.runTest {
+ val isOnKeyguard by collectLastValue(underTest.isOnKeyguard)
+
+ // Shade not opened.
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ // Transitioning away from lock screen.
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+
+ keyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ transitionState = TransitionState.RUNNING,
+ value = 0.5f,
+ )
+ assertThat(isOnKeyguard).isEqualTo(false)
+
+ // Transitioned to bouncer.
+ keyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ transitionState = TransitionState.FINISHED,
+ value = 1f,
+ )
+ assertThat(isOnKeyguard).isEqualTo(false)
+ }
+
+ @Test
+ fun isOnKeyguard_isFalse_whenTransitioningToLockscreenIsRunning() =
+ testScope.runTest {
+ val isOnKeyguard by collectLastValue(underTest.isOnKeyguard)
+
+ // Shade is not opened.
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ // Starts transitioning to lock screen.
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ assertThat(isOnKeyguard).isEqualTo(false)
+
+ keyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.RUNNING,
+ value = 0.5f,
+ )
+ assertThat(isOnKeyguard).isEqualTo(false)
+
+ // Transition has finished.
+ keyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.FINISHED,
+ value = 1f,
+ )
+ assertThat(isOnKeyguard).isEqualTo(true)
+ }
+
+ @Test
+ fun isOnKeyguard_isTrue_whenKeyguardStateIsLockscreen_andShadeIsNotOpened() =
+ testScope.runTest {
+ val isOnKeyguard by collectLastValue(underTest.isOnKeyguard)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ )
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+
+ assertThat(isOnKeyguard).isEqualTo(true)
+ }
+
+ @Test
+ fun isOnKeyguard_isFalse_whenKeyguardStateIsLockscreen_andShadeOpened() =
+ testScope.runTest {
+ val isOnKeyguard by collectLastValue(underTest.isOnKeyguard)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ )
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+
+ assertThat(isOnKeyguard).isEqualTo(false)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelTest.kt
index 78bdfb3..5bf0f4b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelTest.kt
@@ -36,6 +36,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,16 +46,19 @@
class AlternateBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
val kosmos =
testKosmos().apply {
- fakeFeatureFlagsClassic.apply {
- set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
- set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
+
private val testScope = kosmos.testScope
private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
private val sysuiStatusBarStateController = kosmos.sysuiStatusBarStateController
private val underTest by lazy { kosmos.alternateBouncerToGoneTransitionViewModel }
+ @Before
+ fun setup() {
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT)
+ }
+
@Test
fun deviceEntryParentViewDisappear() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
index ff41ea2..854a478 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
@@ -32,6 +32,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -41,15 +42,17 @@
class AlternateBouncerToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- fakeFeatureFlagsClassic.apply {
- set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
- set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
private val testScope = kosmos.testScope
private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
private val underTest by lazy { kosmos.alternateBouncerToPrimaryBouncerTransitionViewModel }
+ @Before
+ fun setup() {
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT)
+ }
+
@Test
fun deviceEntryParentViewDisappear() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
index e6b3017..ee217a6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
@@ -57,10 +57,7 @@
private val kosmos =
testKosmos().apply {
- fakeFeatureFlagsClassic.apply {
- set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
- set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
private val testScope = kosmos.testScope
private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
@@ -72,6 +69,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT)
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(false)
sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(false)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index fc604aa..e3eca67 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -30,11 +30,8 @@
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.fakeFeatureFlagsClassic
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.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -60,13 +57,9 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardRootViewModelTest : SysuiTestCase() {
- private val kosmos =
- testKosmos().apply {
- fakeFeatureFlagsClassic.apply { set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false) }
- }
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
- private val keyguardInteractor = kosmos.keyguardInteractor
private val keyguardRepository = kosmos.fakeKeyguardRepository
private val communalRepository = kosmos.communalRepository
private val screenOffAnimationController = kosmos.screenOffAnimationController
@@ -82,7 +75,10 @@
fun setUp() {
mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION)
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+ mSetFlagsRule.disableFlags(
+ AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
+ AConfigFlags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT,
+ )
}
@Test
@@ -426,4 +422,23 @@
shadeRepository.setQsExpansion(0.5f)
assertThat(alpha).isEqualTo(0f)
}
+
+ @Test
+ fun alpha_idleOnDream_isZero() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+ assertThat(alpha).isEqualTo(1f)
+
+ // Go to GONE state
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING,
+ testScope = testScope,
+ )
+ assertThat(alpha).isEqualTo(0f)
+
+ // Try pulling down shade and ensure the value doesn't change
+ shadeRepository.setQsExpansion(0.5f)
+ assertThat(alpha).isEqualTo(0f)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index e9a8257..3497183 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -21,17 +21,21 @@
import com.android.keyguard.KeyguardClockSwitch
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.authController
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.testKosmos
+import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import java.util.Locale
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -137,4 +141,47 @@
.isFalse()
}
}
+
+ @Test
+ fun unfoldTranslations() =
+ with(kosmos) {
+ testScope.runTest {
+ val maxTranslation = prepareConfiguration()
+ val translations by collectLastValue(underTest.unfoldTranslations)
+
+ val unfoldProvider = fakeUnfoldTransitionProgressProvider
+ unfoldProvider.onTransitionStarted()
+ assertThat(translations?.start).isEqualTo(0f)
+ assertThat(translations?.end).isEqualTo(-0f)
+
+ repeat(10) { repetition ->
+ val transitionProgress = 0.1f * (repetition + 1)
+ unfoldProvider.onTransitionProgress(transitionProgress)
+ assertThat(translations?.start)
+ .isEqualTo((1 - transitionProgress) * maxTranslation)
+ assertThat(translations?.end)
+ .isEqualTo(-(1 - transitionProgress) * maxTranslation)
+ }
+
+ unfoldProvider.onTransitionFinishing()
+ assertThat(translations?.start).isEqualTo(0f)
+ assertThat(translations?.end).isEqualTo(-0f)
+
+ unfoldProvider.onTransitionFinished()
+ assertThat(translations?.start).isEqualTo(0f)
+ assertThat(translations?.end).isEqualTo(-0f)
+ }
+ }
+
+ private fun prepareConfiguration(): Int {
+ val configuration = context.resources.configuration
+ configuration.setLayoutDirection(Locale.US)
+ kosmos.fakeConfigurationRepository.onConfigurationChange(configuration)
+ val maxTranslation = 10
+ kosmos.fakeConfigurationRepository.setDimensionPixelSize(
+ R.dimen.notification_side_paddings,
+ maxTranslation,
+ )
+ return maxTranslation
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 66f7e01..776f1a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -34,6 +34,8 @@
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.shadeRepository
@@ -43,6 +45,7 @@
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
+import kotlin.math.pow
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.BeforeClass
@@ -57,22 +60,26 @@
class LockscreenSceneViewModelTest : SysuiTestCase() {
companion object {
+ private const val parameterCount = 6
+
@Parameters(
name =
"canSwipeToEnter={0}, downWithTwoPointers={1}, downFromEdge={2}," +
- " isSingleShade={3}, isCommunalAvailable={4}"
+ " isSingleShade={3}, isCommunalAvailable={4}, isShadeTouchable={5}"
)
@JvmStatic
fun combinations() = buildList {
- repeat(32) { combination ->
+ repeat(2f.pow(parameterCount).toInt()) { combination ->
add(
arrayOf(
- /* canSwipeToEnter= */ combination and 1 != 0,
- /* downWithTwoPointers= */ combination and 2 != 0,
- /* downFromEdge= */ combination and 4 != 0,
- /* isSingleShade= */ combination and 8 != 0,
- /* isCommunalAvailable= */ combination and 16 != 0,
- )
+ /* canSwipeToEnter= */ combination and 1 != 0,
+ /* downWithTwoPointers= */ combination and 2 != 0,
+ /* downFromEdge= */ combination and 4 != 0,
+ /* isSingleShade= */ combination and 8 != 0,
+ /* isCommunalAvailable= */ combination and 16 != 0,
+ /* isShadeTouchable= */ combination and 32 != 0,
+ )
+ .also { check(it.size == parameterCount) }
)
}
}
@@ -82,8 +89,15 @@
fun setUp() {
val combinationStrings =
combinations().map { array ->
- check(array.size == 5)
- "${array[4]},${array[3]},${array[2]},${array[1]},${array[0]}"
+ check(array.size == parameterCount)
+ buildString {
+ ((parameterCount - 1) downTo 0).forEach { index ->
+ append("${array[index]}")
+ if (index > 0) {
+ append(",")
+ }
+ }
+ }
}
val uniqueCombinations = combinationStrings.toSet()
assertThat(combinationStrings).hasSize(uniqueCombinations.size)
@@ -92,8 +106,35 @@
private fun expectedDownDestination(
downFromEdge: Boolean,
isSingleShade: Boolean,
- ): SceneKey {
- return if (downFromEdge && isSingleShade) Scenes.QuickSettings else Scenes.Shade
+ isShadeTouchable: Boolean,
+ ): SceneKey? {
+ return when {
+ !isShadeTouchable -> null
+ downFromEdge && isSingleShade -> Scenes.QuickSettings
+ else -> Scenes.Shade
+ }
+ }
+
+ private fun expectedUpDestination(
+ canSwipeToEnter: Boolean,
+ isShadeTouchable: Boolean,
+ ): SceneKey? {
+ return when {
+ !isShadeTouchable -> null
+ canSwipeToEnter -> Scenes.Gone
+ else -> Scenes.Bouncer
+ }
+ }
+
+ private fun expectedLeftDestination(
+ isCommunalAvailable: Boolean,
+ isShadeTouchable: Boolean,
+ ): SceneKey? {
+ return when {
+ !isShadeTouchable -> null
+ isCommunalAvailable -> Scenes.Communal
+ else -> null
+ }
}
}
@@ -106,6 +147,7 @@
@JvmField @Parameter(2) var downFromEdge: Boolean = false
@JvmField @Parameter(3) var isSingleShade: Boolean = true
@JvmField @Parameter(4) var isCommunalAvailable: Boolean = false
+ @JvmField @Parameter(5) var isShadeTouchable: Boolean = false
private val underTest by lazy { createLockscreenSceneViewModel() }
@@ -130,6 +172,14 @@
}
)
kosmos.setCommunalAvailable(isCommunalAvailable)
+ kosmos.fakePowerRepository.updateWakefulness(
+ rawState =
+ if (isShadeTouchable) {
+ WakefulnessState.AWAKE
+ } else {
+ WakefulnessState.ASLEEP
+ },
+ )
val destinationScenes by collectLastValue(underTest.destinationScenes)
@@ -148,14 +198,25 @@
expectedDownDestination(
downFromEdge = downFromEdge,
isSingleShade = isSingleShade,
+ isShadeTouchable = isShadeTouchable,
)
)
assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene)
- .isEqualTo(if (canSwipeToEnter) Scenes.Gone else Scenes.Bouncer)
+ .isEqualTo(
+ expectedUpDestination(
+ canSwipeToEnter = canSwipeToEnter,
+ isShadeTouchable = isShadeTouchable,
+ )
+ )
assertThat(destinationScenes?.get(Swipe(SwipeDirection.Left))?.toScene)
- .isEqualTo(Scenes.Communal.takeIf { isCommunalAvailable })
+ .isEqualTo(
+ expectedLeftDestination(
+ isCommunalAvailable = isCommunalAvailable,
+ isShadeTouchable = isShadeTouchable,
+ )
+ )
}
private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index dddf648..4c16a33 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -49,9 +49,7 @@
val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
val configurationRepository = kosmos.fakeConfigurationRepository
- val underTest by lazy {
- kosmos.occludedToLockscreenTransitionViewModel
- }
+ val underTest by lazy { kosmos.occludedToLockscreenTransitionViewModel }
@Test
fun lockscreenFadeIn() =
@@ -164,25 +162,6 @@
values.forEach { assertThat(it).isEqualTo(1f) }
}
- @Test
- fun deviceEntryBackgroundView_noUdfpsEnrolled_noUpdates() =
- testScope.runTest {
- fingerprintPropertyRepository.supportsRearFps()
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
- val values by collectValues(underTest.deviceEntryBackgroundViewAlpha)
-
- keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
- keyguardTransitionRepository.sendTransitionStep(step(0.1f))
- keyguardTransitionRepository.sendTransitionStep(step(0.3f))
- keyguardTransitionRepository.sendTransitionStep(step(0.4f))
- keyguardTransitionRepository.sendTransitionStep(step(0.5f))
- keyguardTransitionRepository.sendTransitionStep(step(0.6f))
- keyguardTransitionRepository.sendTransitionStep(step(0.8f))
- keyguardTransitionRepository.sendTransitionStep(step(1f))
-
- assertThat(values).isEmpty() // no updates
- }
-
private fun step(
value: Float,
state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index db1d5d9..18e9009 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -44,10 +44,7 @@
class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
val kosmos =
testKosmos().apply {
- fakeFeatureFlagsClassic.apply {
- set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
- set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
val testScope = kosmos.testScope
@@ -58,6 +55,7 @@
@Before
fun setUp() {
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT)
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(false)
sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(false)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
index 956ef66..f685058 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
@@ -25,8 +25,11 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.MediaTestHelper
+import com.android.systemui.media.controls.shared.model.MediaCommonModel
import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
@@ -144,7 +147,173 @@
assertThat(smartspaceMediaData?.isActive).isFalse()
}
+ @Test
+ fun addMediaDataLoadingState() =
+ testScope.runTest {
+ val mediaDataLoadedStates by collectLastValue(underTest.mediaDataLoadedStates)
+ val instanceId = InstanceId.fakeInstanceId(123)
+ val mediaLoadedStates = mutableListOf(MediaDataLoadingModel.Loaded(instanceId))
+
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStates)
+
+ mediaLoadedStates.remove(MediaDataLoadingModel.Loaded(instanceId))
+
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Removed(instanceId))
+
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStates)
+ }
+
+ @Test
+ fun setRecommendationsLoadingState() =
+ testScope.runTest {
+ val recommendationsLoadingState by
+ collectLastValue(underTest.recommendationsLoadingState)
+ val recommendationsLoadingModel =
+ SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
+
+ underTest.setRecommendationsLoadingState(recommendationsLoadingModel)
+
+ assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
+ }
+
+ @Test
+ fun addMediaControlPlayingThenRemote() =
+ testScope.runTest {
+ val sortedMedia by collectLastValue(underTest.sortedMedia)
+ val playingInstanceId = InstanceId.fakeInstanceId(123)
+ val remoteInstanceId = InstanceId.fakeInstanceId(321)
+ val playingData = createMediaData("app1", true, LOCAL, false, playingInstanceId)
+ val remoteData = createMediaData("app2", true, REMOTE, false, remoteInstanceId)
+
+ underTest.addSelectedUserMediaEntry(playingData)
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId))
+ underTest.addSelectedUserMediaEntry(remoteData)
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(remoteInstanceId))
+
+ assertThat(sortedMedia?.size).isEqualTo(2)
+ assertThat(sortedMedia?.values)
+ .containsExactly(
+ MediaCommonModel.MediaControl(playingInstanceId),
+ MediaCommonModel.MediaControl(remoteInstanceId)
+ )
+ }
+
+ @Test
+ fun switchMediaControlsPlaying() =
+ testScope.runTest {
+ val sortedMedia by collectLastValue(underTest.sortedMedia)
+ val playingInstanceId1 = InstanceId.fakeInstanceId(123)
+ val playingInstanceId2 = InstanceId.fakeInstanceId(321)
+ var playingData1 = createMediaData("app1", true, LOCAL, false, playingInstanceId1)
+ var playingData2 = createMediaData("app2", false, LOCAL, false, playingInstanceId2)
+
+ underTest.addSelectedUserMediaEntry(playingData1)
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId1))
+ underTest.addSelectedUserMediaEntry(playingData2)
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId2))
+
+ assertThat(sortedMedia?.size).isEqualTo(2)
+ assertThat(sortedMedia?.values)
+ .containsExactly(
+ MediaCommonModel.MediaControl(playingInstanceId1),
+ MediaCommonModel.MediaControl(playingInstanceId2)
+ )
+ .inOrder()
+
+ playingData1 = createMediaData("app1", false, LOCAL, false, playingInstanceId1)
+ playingData2 = createMediaData("app2", true, LOCAL, false, playingInstanceId2)
+
+ underTest.addSelectedUserMediaEntry(playingData1)
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId1))
+ underTest.addSelectedUserMediaEntry(playingData2)
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId2))
+
+ assertThat(sortedMedia?.size).isEqualTo(2)
+ assertThat(sortedMedia?.values)
+ .containsExactly(
+ MediaCommonModel.MediaControl(playingInstanceId2),
+ MediaCommonModel.MediaControl(playingInstanceId1)
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun fullOrderTest() =
+ testScope.runTest {
+ val sortedMedia by collectLastValue(underTest.sortedMedia)
+ val instanceId1 = InstanceId.fakeInstanceId(123)
+ val instanceId2 = InstanceId.fakeInstanceId(456)
+ val instanceId3 = InstanceId.fakeInstanceId(321)
+ val instanceId4 = InstanceId.fakeInstanceId(654)
+ val instanceId5 = InstanceId.fakeInstanceId(124)
+ val playingAndLocalData = createMediaData("app1", true, LOCAL, false, instanceId1)
+ val playingAndRemoteData = createMediaData("app2", true, REMOTE, false, instanceId2)
+ val stoppedAndLocalData = createMediaData("app3", false, LOCAL, false, instanceId3)
+ val stoppedAndRemoteData = createMediaData("app4", false, REMOTE, false, instanceId4)
+ val canResumeData = createMediaData("app5", false, LOCAL, true, instanceId5)
+
+ val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
+ val mediaRecommendations =
+ SmartspaceMediaData(
+ targetId = KEY_MEDIA_SMARTSPACE,
+ isActive = true,
+ recommendations = MediaTestHelper.getValidRecommendationList(icon),
+ )
+
+ underTest.addSelectedUserMediaEntry(stoppedAndLocalData)
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId3))
+
+ underTest.addSelectedUserMediaEntry(stoppedAndRemoteData)
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId4))
+
+ underTest.addSelectedUserMediaEntry(canResumeData)
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId5))
+
+ underTest.addSelectedUserMediaEntry(playingAndLocalData)
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId1))
+
+ underTest.addSelectedUserMediaEntry(playingAndRemoteData)
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId2))
+
+ underTest.setRecommendation(mediaRecommendations)
+ underTest.setRecommendationsLoadingState(
+ SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
+ )
+
+ assertThat(sortedMedia?.size).isEqualTo(6)
+ assertThat(sortedMedia?.values)
+ .containsExactly(
+ MediaCommonModel.MediaControl(instanceId1),
+ MediaCommonModel.MediaControl(instanceId2),
+ MediaCommonModel.MediaRecommendations(KEY_MEDIA_SMARTSPACE),
+ MediaCommonModel.MediaControl(instanceId4),
+ MediaCommonModel.MediaControl(instanceId3),
+ MediaCommonModel.MediaControl(instanceId5),
+ )
+ .inOrder()
+ }
+
+ private fun createMediaData(
+ app: String,
+ playing: Boolean,
+ playbackLocation: Int,
+ isResume: Boolean,
+ instanceId: InstanceId,
+ ): MediaData {
+ return MediaData(
+ playbackLocation = playbackLocation,
+ resumption = isResume,
+ notificationKey = "key: $app",
+ isPlaying = playing,
+ instanceId = instanceId
+ )
+ }
+
companion object {
+ private const val LOCAL = MediaData.PLAYBACK_LOCAL
+ private const val REMOTE = MediaData.PLAYBACK_CAST_LOCAL
private const val KEY = "KEY"
private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
index d9d84f2..c15776e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
@@ -20,6 +20,7 @@
import android.graphics.drawable.Icon
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
@@ -28,13 +29,21 @@
import com.android.systemui.media.controls.MediaTestHelper
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
+import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
+import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
+import com.android.systemui.media.controls.shared.model.MediaCommonModel
import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
+import com.android.systemui.statusbar.notificationLockscreenUserManager
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,9 +54,17 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
+ private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter
+ private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager
private val mediaFilterRepository: MediaFilterRepository = kosmos.mediaFilterRepository
+
private val underTest: MediaCarouselInteractor = kosmos.mediaCarouselInteractor
+ @Before
+ fun setUp() {
+ underTest.start()
+ }
+
@Test
fun addUserMediaEntry_activeThenInactivate() =
testScope.runTest {
@@ -56,7 +73,7 @@
val hasActiveMedia by collectLastValue(underTest.hasActiveMedia)
val hasAnyMedia by collectLastValue(underTest.hasAnyMedia)
- val userMedia = MediaData().copy(active = true)
+ val userMedia = MediaData(active = true)
mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
@@ -78,22 +95,29 @@
collectLastValue(underTest.hasActiveMediaOrRecommendation)
val hasActiveMedia by collectLastValue(underTest.hasActiveMedia)
val hasAnyMedia by collectLastValue(underTest.hasAnyMedia)
+ val sortedMedia by collectLastValue(underTest.sortedMedia)
- val userMedia = MediaData().copy(active = false)
+ val userMedia = MediaData(active = false)
val instanceId = userMedia.instanceId
mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
assertThat(hasActiveMediaOrRecommendation).isFalse()
assertThat(hasActiveMedia).isFalse()
assertThat(hasAnyMedia).isTrue()
+ assertThat(sortedMedia).containsExactly(MediaCommonModel.MediaControl(instanceId))
assertThat(mediaFilterRepository.removeSelectedUserMediaEntry(instanceId, userMedia))
.isTrue()
+ mediaFilterRepository.addMediaDataLoadingState(
+ MediaDataLoadingModel.Removed(instanceId)
+ )
assertThat(hasActiveMediaOrRecommendation).isFalse()
assertThat(hasActiveMedia).isFalse()
assertThat(hasAnyMedia).isFalse()
+ assertThat(sortedMedia).isEmpty()
}
@Test
@@ -103,6 +127,7 @@
collectLastValue(underTest.hasActiveMediaOrRecommendation)
val hasAnyMediaOrRecommendation by
collectLastValue(underTest.hasAnyMediaOrRecommendation)
+ val sortedMedia by collectLastValue(underTest.sortedMedia)
kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
@@ -112,17 +137,31 @@
isActive = true,
recommendations = MediaTestHelper.getValidRecommendationList(icon),
)
- val userMedia = MediaData().copy(active = false)
+ val userMedia = MediaData(active = false)
mediaFilterRepository.setRecommendation(userMediaRecommendation)
+ mediaFilterRepository.setRecommendationsLoadingState(
+ SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
+ )
assertThat(hasActiveMediaOrRecommendation).isTrue()
assertThat(hasAnyMediaOrRecommendation).isTrue()
+ assertThat(sortedMedia)
+ .containsExactly(MediaCommonModel.MediaRecommendations(KEY_MEDIA_SMARTSPACE))
mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ mediaFilterRepository.addMediaDataLoadingState(
+ MediaDataLoadingModel.Loaded(userMedia.instanceId)
+ )
assertThat(hasActiveMediaOrRecommendation).isTrue()
assertThat(hasAnyMediaOrRecommendation).isTrue()
+ assertThat(sortedMedia)
+ .containsExactly(
+ MediaCommonModel.MediaRecommendations(KEY_MEDIA_SMARTSPACE),
+ MediaCommonModel.MediaControl(userMedia.instanceId)
+ )
+ .inOrder()
}
@Test
@@ -199,7 +238,80 @@
fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() =
testScope.runTest { assertThat(underTest.hasActiveMediaOrRecommendation.value).isFalse() }
+ @Test
+ fun onMediaDataUpdated_updatesLoadingState() =
+ testScope.runTest {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
+ val mediaDataLoadedStates by collectLastValue(underTest.mediaDataLoadedStates)
+ val instanceId = InstanceId.fakeInstanceId(123)
+ val mediaLoadedStates: MutableList<MediaDataLoadingModel> = mutableListOf()
+
+ mediaLoadedStates.add(MediaDataLoadingModel.Loaded(instanceId))
+ mediaDataFilter.onMediaDataLoaded(
+ KEY,
+ KEY,
+ MediaData(userId = USER_ID, instanceId = instanceId)
+ )
+
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStates)
+
+ val newInstanceId = InstanceId.fakeInstanceId(321)
+
+ mediaLoadedStates.add(MediaDataLoadingModel.Loaded(newInstanceId))
+ mediaDataFilter.onMediaDataLoaded(
+ KEY_2,
+ KEY_2,
+ MediaData(userId = USER_ID, instanceId = newInstanceId)
+ )
+
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStates)
+
+ mediaLoadedStates.remove(MediaDataLoadingModel.Loaded(instanceId))
+
+ mediaDataFilter.onMediaDataRemoved(KEY)
+
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStates)
+
+ mediaLoadedStates.remove(MediaDataLoadingModel.Loaded(newInstanceId))
+
+ mediaDataFilter.onMediaDataRemoved(KEY_2)
+
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStates)
+ }
+
+ @Test
+ fun onMediaRecommendationsUpdated_updatesLoadingState() =
+ testScope.runTest {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
+ val recommendationsLoadingState by
+ collectLastValue(underTest.recommendationsLoadingState)
+ val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
+ val mediaRecommendations =
+ SmartspaceMediaData(
+ targetId = KEY_MEDIA_SMARTSPACE,
+ isActive = true,
+ recommendations = MediaTestHelper.getValidRecommendationList(icon),
+ )
+ var recommendationsLoadingModel: SmartspaceMediaLoadingModel =
+ SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, isPrioritized = true)
+
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, mediaRecommendations)
+
+ assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
+
+ recommendationsLoadingModel = SmartspaceMediaLoadingModel.Removed(KEY_MEDIA_SMARTSPACE)
+
+ mediaDataFilter.onSmartspaceMediaDataRemoved(KEY_MEDIA_SMARTSPACE)
+
+ assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
+ }
+
companion object {
+ private const val KEY = "key"
+ private const val KEY_2 = "key2"
+ private const val USER_ID = 0
private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
index a1cee8a..d9224d7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
@@ -16,10 +16,17 @@
package com.android.systemui.media.controls.domain.interactor
+import android.app.PendingIntent
+import android.os.Bundle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
+import com.android.systemui.activityIntentHelper
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
+import com.android.systemui.bluetooth.mockBroadcastDialogController
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
@@ -28,13 +35,22 @@
import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.util.mediaInstanceId
+import com.android.systemui.media.mediaOutputDialogManager
+import com.android.systemui.mockActivityIntentHelper
+import com.android.systemui.plugins.activityStarter
import com.android.systemui.statusbar.notificationLockscreenUserManager
+import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -44,10 +60,16 @@
private val testScope = kosmos.testScope
private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter
+ private val activityStarter = kosmos.activityStarter
+ private val keyguardStateController = kosmos.keyguardStateController
private val instanceId: InstanceId = kosmos.mediaInstanceId
private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager
- private val underTest: MediaControlInteractor = kosmos.mediaControlInteractor
+ private val underTest: MediaControlInteractor =
+ with(kosmos) {
+ activityIntentHelper = mockActivityIntentHelper
+ kosmos.mediaControlInteractor
+ }
@Test
fun onMediaDataUpdated() =
@@ -55,39 +77,146 @@
whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
val controlModel by collectLastValue(underTest.mediaControl)
- var mediaData =
- MediaData(userId = USER_ID, instanceId = instanceId, artist = SESSION_ARTIST)
+ var mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
assertThat(controlModel?.instanceId).isEqualTo(instanceId)
- assertThat(controlModel?.artistName).isEqualTo(SESSION_ARTIST)
+ assertThat(controlModel?.artistName).isEqualTo(ARTIST)
- mediaData =
- MediaData(userId = USER_ID, instanceId = instanceId, artist = SESSION_ARTIST_2)
+ mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST_2)
mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
assertThat(controlModel?.instanceId).isEqualTo(instanceId)
- assertThat(controlModel?.artistName).isEqualTo(SESSION_ARTIST_2)
+ assertThat(controlModel?.artistName).isEqualTo(ARTIST_2)
mediaData =
MediaData(
userId = USER_ID,
instanceId = InstanceId.fakeInstanceId(2),
- artist = SESSION_ARTIST
+ artist = ARTIST
)
mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
assertThat(controlModel?.instanceId).isNotEqualTo(mediaData.instanceId)
- assertThat(controlModel?.artistName).isEqualTo(SESSION_ARTIST_2)
+ assertThat(controlModel?.artistName).isEqualTo(ARTIST_2)
}
+ @Test
+ fun startSettings() {
+ underTest.startSettings()
+
+ verify(activityStarter).startActivity(any(), eq(true))
+ }
+
+ @Test
+ fun startClickIntent_showOverLockscreen() {
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+ whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any()))
+ .thenReturn(true)
+
+ val clickIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+ val expandable = mock<Expandable>()
+
+ underTest.startClickIntent(expandable, clickIntent)
+
+ verify(clickIntent).send(any<Bundle>())
+ }
+
+ @Test
+ fun startClickIntent_hideOverLockscreen() {
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+
+ val clickIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+ val expandable = mock<Expandable>()
+ val activityController = mock<ActivityTransitionAnimator.Controller>()
+ whenever(expandable.activityTransitionController(any())).thenReturn(activityController)
+
+ underTest.startClickIntent(expandable, clickIntent)
+
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(eq(clickIntent), eq(activityController))
+ }
+
+ @Test
+ fun startDeviceIntent_showOverLockscreen() {
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+ whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any()))
+ .thenReturn(true)
+
+ val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+
+ underTest.startDeviceIntent(deviceIntent)
+
+ verify(deviceIntent).send(any<Bundle>())
+ }
+
+ @Test
+ fun startDeviceIntent_intentNotActivity() {
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+ whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any()))
+ .thenReturn(true)
+
+ val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(false) }
+
+ underTest.startDeviceIntent(deviceIntent)
+
+ verify(deviceIntent, never()).send(any<Bundle>())
+ }
+
+ @Test
+ fun startDeviceIntent_hideOverLockscreen() {
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+
+ val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+
+ underTest.startDeviceIntent(deviceIntent)
+
+ verify(activityStarter).postStartActivityDismissingKeyguard(eq(deviceIntent))
+ }
+
+ @Test
+ fun startMediaOutputDialog() {
+ val expandable = mock<Expandable>()
+ val dialogTransitionController = mock<DialogTransitionAnimator.Controller>()
+ whenever(expandable.dialogTransitionController(any()))
+ .thenReturn(dialogTransitionController)
+
+ underTest.startMediaOutputDialog(expandable, PACKAGE_NAME)
+
+ verify(kosmos.mediaOutputDialogManager)
+ .createAndShowWithController(
+ eq(PACKAGE_NAME),
+ eq(true),
+ eq(dialogTransitionController),
+ eq(null)
+ )
+ }
+
+ @Test
+ fun startBroadcastDialog() {
+ val expandable = mock<Expandable>()
+ val dialogTransitionController = mock<DialogTransitionAnimator.Controller>()
+ whenever(expandable.dialogTransitionController()).thenReturn(dialogTransitionController)
+
+ underTest.startBroadcastDialog(expandable, APP_NAME, PACKAGE_NAME)
+
+ verify(kosmos.mockBroadcastDialogController)
+ .createBroadcastDialogWithController(
+ eq(APP_NAME),
+ eq(PACKAGE_NAME),
+ eq(dialogTransitionController)
+ )
+ }
+
companion object {
private const val USER_ID = 0
private const val KEY = "key"
- private const val SESSION_ARTIST = "artist"
- private const val SESSION_ARTIST_2 = "artist2"
+ private const val PACKAGE_NAME = "com.example.app"
+ private const val APP_NAME = "app"
+ private const val ARTIST = "artist"
+ private const val ARTIST_2 = "artist2"
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt
new file mode 100644
index 0000000..9558e5d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.ui.viewmodel
+
+import android.R
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
+import android.media.MediaMetadata
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
+import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.util.mediaInstanceId
+import com.android.systemui.statusbar.notificationLockscreenUserManager
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MediaControlViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter
+ private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager
+ private val packageManager = kosmos.packageManager
+ private val drawable = context.getDrawable(R.drawable.ic_media_play)
+ private val instanceId: InstanceId = kosmos.mediaInstanceId
+
+ private val underTest: MediaControlViewModel = kosmos.mediaControlViewModel
+
+ @Test
+ fun addMediaControl_mediaControlViewModelIsLoaded() =
+ testScope.runTest {
+ whenever(packageManager.getApplicationIcon(Mockito.anyString())).thenReturn(drawable)
+ whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java)))
+ .thenReturn(drawable)
+ whenever(packageManager.getApplicationInfo(eq(PACKAGE_NAME), ArgumentMatchers.anyInt()))
+ .thenReturn(ApplicationInfo())
+ whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE_NAME)
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
+ val playerModel by collectLastValue(underTest.player)
+
+ context.setMockPackageManager(packageManager)
+
+ val mediaData = initMediaData()
+
+ mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
+
+ assertThat(playerModel).isNotNull()
+ assertThat(playerModel?.titleName).isEqualTo(TITLE)
+ assertThat(playerModel?.artistName).isEqualTo(ARTIST)
+ assertThat(playerModel?.gutsMenu).isNotNull()
+ assertThat(playerModel?.outputSwitcher).isNotNull()
+ assertThat(playerModel?.actionButtons).isNotNull()
+ assertThat(playerModel?.playTurbulenceNoise).isFalse()
+ }
+
+ private fun initMediaData(): MediaData {
+ val device = MediaDeviceData(true, null, DEVICE_NAME, null, showBroadcastButton = true)
+
+ // Create media session
+ val metadataBuilder =
+ MediaMetadata.Builder().apply {
+ putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
+ putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
+ }
+ val playbackBuilder =
+ PlaybackState.Builder().apply {
+ setState(PlaybackState.STATE_PAUSED, 6000L, 1f)
+ setActions(PlaybackState.ACTION_PLAY)
+ }
+ val session =
+ MediaSession(context, SESSION_KEY).apply {
+ setMetadata(metadataBuilder.build())
+ setPlaybackState(playbackBuilder.build())
+ }
+ session.isActive = true
+
+ return MediaData(
+ userId = USER_ID,
+ artist = ARTIST,
+ song = TITLE,
+ packageName = PACKAGE,
+ token = session.sessionToken,
+ device = device,
+ instanceId = instanceId
+ )
+ }
+
+ companion object {
+ private const val USER_ID = 0
+ private const val KEY = "key"
+ private const val PACKAGE_NAME = "com.example.app"
+ private const val PACKAGE = "PKG"
+ private const val ARTIST = "ARTIST"
+ private const val TITLE = "TITLE"
+ private const val DEVICE_NAME = "DEVICE_NAME"
+ private const val SESSION_KEY = "SESSION_KEY"
+ private const val SESSION_ARTIST = "SESSION_ARTIST"
+ private const val SESSION_TITLE = "SESSION_TITLE"
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
index 6d6fd75..d0699aa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
-import android.platform.test.annotations.EnabledOnRavenwood
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.view.accessibility.Flags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -44,7 +46,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class ReduceBrightColorsAutoAddableTest : SysuiTestCase() {
@@ -67,12 +68,14 @@
}
@Test
+ @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
fun available_strategyIfNotAdded() =
testWithFeatureAvailability(available = true) {
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.IfNotAdded(SPEC))
}
@Test
+ @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
fun activated_addSignal() = testWithFeatureAvailability {
val signal by collectLastValue(underTest.autoAddSignal(0))
runCurrent()
@@ -85,6 +88,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
fun notActivated_noSignal() = testWithFeatureAvailability {
val signal by collectLastValue(underTest.autoAddSignal(0))
runCurrent()
@@ -96,6 +100,13 @@
assertThat(signal).isNull()
}
+ @Test
+ @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
+ fun available_a11yQsShortcutFlagEnabled_strategyDisabled() =
+ testWithFeatureAvailability(available = true) {
+ assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Disabled)
+ }
+
private fun testWithFeatureAvailability(
available: Boolean = true,
body: suspend TestScope.() -> TestResult
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
new file mode 100644
index 0000000..2e5fde8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.reducebrightness.domain.interactor
+
+import android.os.UserHandle
+import android.platform.test.annotations.EnabledOnRavenwood
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.reduceBrightColorsController
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.toCollection
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class ReduceBrightColorsTileDataInteractorTest : SysuiTestCase() {
+
+ private val isAvailable = true
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val reduceBrightColorsController = kosmos.reduceBrightColorsController
+ private val underTest: ReduceBrightColorsTileDataInteractor =
+ ReduceBrightColorsTileDataInteractor(
+ testScope.testScheduler,
+ isAvailable,
+ reduceBrightColorsController
+ )
+
+ @Test
+ fun alwaysAvailable() =
+ testScope.runTest {
+ val availability = underTest.availability(TEST_USER).toCollection(mutableListOf())
+
+ assertThat(availability).hasSize(1)
+ assertThat(availability.last()).isEqualTo(isAvailable)
+ }
+
+ @Test
+ fun dataMatchesTheRepository() =
+ testScope.runTest {
+ val dataList: List<ReduceBrightColorsTileModel> by
+ collectValues(
+ underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+
+ reduceBrightColorsController.isReduceBrightColorsActivated = true
+ runCurrent()
+
+ reduceBrightColorsController.isReduceBrightColorsActivated = false
+ runCurrent()
+
+ assertThat(dataList).hasSize(3)
+ assertThat(dataList.map { it.isEnabled }).isEqualTo(listOf(false, true, false))
+ }
+
+ private companion object {
+ val TEST_USER = UserHandle.of(1)!!
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
new file mode 100644
index 0000000..6ea5e63
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.reducebrightness.domain.interactor
+
+import android.platform.test.annotations.EnabledOnRavenwood
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.reduceBrightColorsController
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class ReduceBrightColorsTileUserActionInteractorTest : SysuiTestCase() {
+
+ private val kosmos = Kosmos()
+ private val inputHandler = FakeQSTileIntentUserInputHandler()
+ private val controller = kosmos.reduceBrightColorsController
+
+ private val underTest =
+ ReduceBrightColorsTileUserActionInteractor(
+ inputHandler,
+ controller,
+ )
+
+ @Test
+ fun handleClickWhenEnabled() = runTest {
+ val wasEnabled = true
+ controller.isReduceBrightColorsActivated = wasEnabled
+
+ underTest.handleInput(QSTileInputTestKtx.click(ReduceBrightColorsTileModel(wasEnabled)))
+
+ assertThat(controller.isReduceBrightColorsActivated).isEqualTo(!wasEnabled)
+ }
+
+ @Test
+ fun handleClickWhenDisabled() = runTest {
+ val wasEnabled = false
+ controller.isReduceBrightColorsActivated = wasEnabled
+
+ underTest.handleInput(QSTileInputTestKtx.click(ReduceBrightColorsTileModel(wasEnabled)))
+
+ assertThat(controller.isReduceBrightColorsActivated).isEqualTo(!wasEnabled)
+ }
+
+ @Test
+ fun handleLongClickWhenDisabled() = runTest {
+ val enabled = false
+
+ underTest.handleInput(QSTileInputTestKtx.longClick(ReduceBrightColorsTileModel(enabled)))
+
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+ assertThat(it.intent.action).isEqualTo(Settings.ACTION_REDUCE_BRIGHT_COLORS_SETTINGS)
+ }
+ }
+
+ @Test
+ fun handleLongClickWhenEnabled() = runTest {
+ val enabled = true
+
+ underTest.handleInput(QSTileInputTestKtx.longClick(ReduceBrightColorsTileModel(enabled)))
+
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+ assertThat(it.intent.action).isEqualTo(Settings.ACTION_REDUCE_BRIGHT_COLORS_SETTINGS)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
new file mode 100644
index 0000000..10e9bd6
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.reducebrightness.ui
+
+import android.graphics.drawable.TestStubDrawable
+import android.service.quicksettings.Tile
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.qs.tiles.impl.reducebrightness.qsReduceBrightColorsTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ReduceBrightColorsTileMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val config = kosmos.qsReduceBrightColorsTileConfig
+
+ private lateinit var mapper: ReduceBrightColorsTileMapper
+
+ @Before
+ fun setup() {
+ mapper =
+ ReduceBrightColorsTileMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(R.drawable.qs_extra_dim_icon_on, TestStubDrawable())
+ addOverride(R.drawable.qs_extra_dim_icon_off, TestStubDrawable())
+ }
+ .resources,
+ context.theme
+ )
+ }
+
+ @Test
+ fun disabledModel() {
+ val inputModel = ReduceBrightColorsTileModel(false)
+
+ val outputState = mapper.map(config, inputModel)
+
+ val expectedState =
+ createReduceBrightColorsTileState(
+ QSTileState.ActivationState.INACTIVE,
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun enabledModel() {
+ val inputModel = ReduceBrightColorsTileModel(true)
+
+ val outputState = mapper.map(config, inputModel)
+
+ val expectedState = createReduceBrightColorsTileState(QSTileState.ActivationState.ACTIVE)
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ private fun createReduceBrightColorsTileState(
+ activationState: QSTileState.ActivationState,
+ ): QSTileState {
+ val label =
+ context.getString(com.android.internal.R.string.reduce_bright_colors_feature_name)
+ return QSTileState(
+ {
+ Icon.Loaded(
+ context.getDrawable(
+ if (activationState == QSTileState.ActivationState.ACTIVE)
+ R.drawable.qs_extra_dim_icon_on
+ else R.drawable.qs_extra_dim_icon_off
+ )!!,
+ null
+ )
+ },
+ label,
+ activationState,
+ context.resources
+ .getStringArray(R.array.tile_states_reduce_brightness)[
+ if (activationState == QSTileState.ActivationState.ACTIVE) Tile.STATE_ACTIVE
+ else Tile.STATE_INACTIVE],
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+ label,
+ null,
+ QSTileState.SideViewIcon.None,
+ QSTileState.EnabledState.ENABLED,
+ Switch::class.qualifiedName
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
new file mode 100644
index 0000000..954f691
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor
+
+import android.os.UserHandle
+import android.platform.test.annotations.EnabledOnRavenwood
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.model.ScreenRecordTileModel
+import com.android.systemui.screenrecord.RecordingController
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class ScreenRecordTileDataInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val controller = mock<RecordingController>()
+ private val underTest: ScreenRecordTileDataInteractor =
+ ScreenRecordTileDataInteractor(testScope.testScheduler, controller)
+
+ private val isRecording = ScreenRecordTileModel.Recording
+ private val isDoingNothing = ScreenRecordTileModel.DoingNothing
+ private val isStarting0 = ScreenRecordTileModel.Starting(0)
+
+ @Test
+ fun isAvailable_returnsTrue() = runTest {
+ val availability by collectLastValue(underTest.availability(TEST_USER))
+
+ assertThat(availability).isTrue()
+ }
+
+ @Test
+ fun dataMatchesController() =
+ testScope.runTest {
+ whenever(controller.isRecording).thenReturn(false)
+ whenever(controller.isStarting).thenReturn(false)
+
+ val callbackCaptor = argumentCaptor<RecordingController.RecordingStateChangeCallback>()
+
+ val lastModel by
+ collectLastValue(
+ underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+
+ verify(controller).addCallback(callbackCaptor.capture())
+ val callback = callbackCaptor.value
+
+ assertThat(lastModel).isEqualTo(isDoingNothing)
+
+ val expectedModelStartingIn1 = ScreenRecordTileModel.Starting(1)
+ callback.onCountdown(1)
+ assertThat(lastModel).isEqualTo(expectedModelStartingIn1)
+
+ val expectedModelStartingIn0 = isStarting0
+ callback.onCountdown(0)
+ assertThat(lastModel).isEqualTo(expectedModelStartingIn0)
+
+ callback.onCountdownEnd()
+ assertThat(lastModel).isEqualTo(isDoingNothing)
+
+ callback.onRecordingStart()
+ assertThat(lastModel).isEqualTo(isRecording)
+
+ callback.onRecordingEnd()
+ assertThat(lastModel).isEqualTo(isDoingNothing)
+ }
+
+ @Test
+ fun data_whenRecording_matchesController() =
+ testScope.runTest {
+ whenever(controller.isRecording).thenReturn(true)
+ whenever(controller.isStarting).thenReturn(false)
+
+ val lastModel by
+ collectLastValue(
+ underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+
+ assertThat(lastModel).isEqualTo(isRecording)
+ }
+
+ @Test
+ fun data_whenStarting_matchesController() =
+ testScope.runTest {
+ whenever(controller.isRecording).thenReturn(false)
+ whenever(controller.isStarting).thenReturn(true)
+
+ val lastModel by
+ collectLastValue(
+ underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+
+ assertThat(lastModel).isEqualTo(isStarting0)
+ }
+
+ @Test
+ fun data_whenRecordingAndStarting_matchesControllerRecording() =
+ testScope.runTest {
+ whenever(controller.isRecording).thenReturn(true)
+ whenever(controller.isStarting).thenReturn(true)
+
+ val lastModel by
+ collectLastValue(
+ underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+
+ assertThat(lastModel).isEqualTo(isRecording)
+ }
+
+ private companion object {
+ val TEST_USER = UserHandle.of(1)!!
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
new file mode 100644
index 0000000..b9321d5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor
+
+import android.app.Dialog
+import android.os.UserHandle
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.dialogTransitionAnimator
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.model.ScreenRecordTileModel
+import com.android.systemui.screenrecord.RecordingController
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
+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.mock
+import com.android.systemui.util.mockito.whenever
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val keyguardInteractor = kosmos.keyguardInteractor
+ private val dialogTransitionAnimator = mock<DialogTransitionAnimator>()
+ private val featureFlags = kosmos.featureFlagsClassic
+ private val activityStarter = kosmos.activityStarter
+ private val keyguardDismissUtil = mock<KeyguardDismissUtil>()
+ private val panelInteractor = mock<PanelInteractor>()
+ private val dialog = mock<Dialog>()
+ private val recordingController =
+ mock<RecordingController> {
+ whenever(
+ createScreenRecordDialog(
+ eq(context),
+ eq(featureFlags),
+ eq(dialogTransitionAnimator),
+ eq(activityStarter),
+ any()
+ )
+ )
+ .thenReturn(dialog)
+ }
+
+ private val underTest =
+ ScreenRecordTileUserActionInteractor(
+ context,
+ testScope.testScheduler,
+ testScope.testScheduler,
+ recordingController,
+ keyguardInteractor,
+ keyguardDismissUtil,
+ dialogTransitionAnimator,
+ panelInteractor,
+ mock<MediaProjectionMetricsLogger>(),
+ featureFlags,
+ activityStarter,
+ )
+
+ @Test
+ fun handleClick_whenStarting_cancelCountdown() = runTest {
+ val startingModel = ScreenRecordTileModel.Starting(0)
+
+ underTest.handleInput(QSTileInputTestKtx.click(startingModel))
+
+ verify(recordingController).cancelCountdown()
+ }
+
+ @Test
+ fun handleClick_whenRecording_stopRecording() = runTest {
+ val recordingModel = ScreenRecordTileModel.Recording
+
+ underTest.handleInput(QSTileInputTestKtx.click(recordingModel))
+
+ verify(recordingController).stopRecording()
+ }
+
+ @Test
+ fun handleClick_whenDoingNothing_createDialogDismissPanelShowDialog() = runTest {
+ val recordingModel = ScreenRecordTileModel.DoingNothing
+
+ underTest.handleInput(QSTileInputTestKtx.click(recordingModel))
+ val onStartRecordingClickedCaptor = argumentCaptor<Runnable>()
+ verify(recordingController)
+ .createScreenRecordDialog(
+ eq(context),
+ eq(featureFlags),
+ eq(dialogTransitionAnimator),
+ eq(activityStarter),
+ onStartRecordingClickedCaptor.capture()
+ )
+
+ val onDismissActionCaptor = argumentCaptor<OnDismissAction>()
+ verify(keyguardDismissUtil)
+ .executeWhenUnlocked(onDismissActionCaptor.capture(), eq(false), eq(true))
+ onDismissActionCaptor.value.onDismiss()
+ verify(dialog).show() // because the view was null
+
+ // When starting the recording, we collapse the shade and disable the dialog animation.
+ onStartRecordingClickedCaptor.value.run()
+ verify(dialogTransitionAnimator).disableAllCurrentDialogsExitAnimations()
+ verify(panelInteractor).collapsePanels()
+ }
+
+ /**
+ * When the input view is not null and keyguard is not showing, dialog should animate and show
+ */
+ @Test
+ fun handleClickFromView_whenDoingNothing_whenKeyguardNotShowing_showDialogFromView() = runTest {
+ val view = mock<View>()
+ kosmos.fakeKeyguardRepository.setKeyguardShowing(false)
+
+ val recordingModel = ScreenRecordTileModel.DoingNothing
+
+ underTest.handleInput(QSTileInputTestKtx.click(recordingModel, UserHandle.CURRENT, view))
+ val onStartRecordingClickedCaptor = argumentCaptor<Runnable>()
+ verify(recordingController)
+ .createScreenRecordDialog(
+ eq(context),
+ eq(featureFlags),
+ eq(dialogTransitionAnimator),
+ eq(activityStarter),
+ onStartRecordingClickedCaptor.capture()
+ )
+
+ val onDismissActionCaptor = argumentCaptor<OnDismissAction>()
+ verify(keyguardDismissUtil)
+ .executeWhenUnlocked(onDismissActionCaptor.capture(), eq(false), eq(true))
+ onDismissActionCaptor.value.onDismiss()
+ verify(dialogTransitionAnimator).showFromView(eq(dialog), eq(view), any(), eq(true))
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
new file mode 100644
index 0000000..d7b7ab6
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.screenrecord.ui
+
+import android.graphics.drawable.TestStubDrawable
+import android.text.TextUtils
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.model.ScreenRecordTileModel
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.ScreenRecordTileMapper
+import com.android.systemui.qs.tiles.impl.screenrecord.qsScreenRecordTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ScreenRecordTileMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val config = kosmos.qsScreenRecordTileConfig
+
+ private lateinit var mapper: ScreenRecordTileMapper
+
+ @Before
+ fun setup() {
+ mapper =
+ ScreenRecordTileMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(R.drawable.qs_screen_record_icon_on, TestStubDrawable())
+ addOverride(R.drawable.qs_screen_record_icon_off, TestStubDrawable())
+ }
+ .resources,
+ context.theme
+ )
+ }
+
+ @Test
+ fun activeStateMatchesRecordingDataModel() {
+ val inputModel = ScreenRecordTileModel.Recording
+
+ val outputState = mapper.map(config, inputModel)
+
+ val expectedState =
+ createScreenRecordTileState(
+ QSTileState.ActivationState.ACTIVE,
+ R.drawable.qs_screen_record_icon_on,
+ context.getString(R.string.quick_settings_screen_record_stop),
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun activeStateMatchesStartingDataModel() {
+ val timeLeft = 0L
+ val inputModel = ScreenRecordTileModel.Starting(timeLeft)
+
+ val outputState = mapper.map(config, inputModel)
+
+ val expectedState =
+ createScreenRecordTileState(
+ QSTileState.ActivationState.ACTIVE,
+ R.drawable.qs_screen_record_icon_on,
+ String.format("%d...", timeLeft)
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun inactiveStateMatchesDisabledDataModel() {
+ val inputModel = ScreenRecordTileModel.DoingNothing
+
+ val outputState = mapper.map(config, inputModel)
+
+ val expectedState =
+ createScreenRecordTileState(
+ QSTileState.ActivationState.INACTIVE,
+ R.drawable.qs_screen_record_icon_off,
+ context.getString(R.string.quick_settings_screen_record_start),
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ private fun createScreenRecordTileState(
+ activationState: QSTileState.ActivationState,
+ iconRes: Int,
+ secondaryLabel: String,
+ ): QSTileState {
+ val label = context.getString(R.string.quick_settings_screen_record_label)
+
+ return QSTileState(
+ { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ label,
+ activationState,
+ secondaryLabel,
+ setOf(QSTileState.UserAction.CLICK),
+ if (TextUtils.isEmpty(secondaryLabel)) label
+ else TextUtils.concat(label, ", ", secondaryLabel),
+ null,
+ if (activationState == QSTileState.ActivationState.INACTIVE)
+ QSTileState.SideViewIcon.Chevron
+ else QSTileState.SideViewIcon.None,
+ QSTileState.EnabledState.ENABLED,
+ Switch::class.qualifiedName
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 139289a..3727c11 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -24,8 +24,8 @@
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.FooterActionsController
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
@@ -34,18 +34,8 @@
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel
-import com.android.systemui.shade.domain.interactor.privacyChipInteractor
-import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
-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.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
-import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -64,8 +54,6 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
- private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
private val footerActionsViewModel = mock<FooterActionsViewModel>()
private val footerActionsViewModelFactory =
@@ -74,45 +62,18 @@
}
private val footerActionsController = mock<FooterActionsController>()
- private var mobileIconsViewModel: MobileIconsViewModel =
- MobileIconsViewModel(
- logger = mock(),
- verboseLogger = mock(),
- interactor = mobileIconsInteractor,
- airplaneModeInteractor =
- AirplaneModeInteractor(
- FakeAirplaneModeRepository(),
- FakeConnectivityRepository(),
- FakeMobileConnectionsRepository(),
- ),
- constants = mock(),
- flags,
- scope = testScope.backgroundScope,
- )
private val sceneInteractor = kosmos.sceneInteractor
- private lateinit var shadeHeaderViewModel: ShadeHeaderViewModel
-
private lateinit var underTest: QuickSettingsSceneViewModel
@Before
fun setUp() {
- shadeHeaderViewModel =
- ShadeHeaderViewModel(
- applicationScope = testScope.backgroundScope,
- context = context,
- shadeInteractor = kosmos.shadeInteractor,
- mobileIconsInteractor = mobileIconsInteractor,
- mobileIconsViewModel = mobileIconsViewModel,
- privacyChipInteractor = kosmos.privacyChipInteractor,
- clockInteractor = kosmos.shadeHeaderClockInteractor,
- broadcastDispatcher = fakeBroadcastDispatcher,
- )
+ kosmos.fakeFeatureFlagsClassic.set(Flags.NEW_NETWORK_SLICE_UI, false)
underTest =
QuickSettingsSceneViewModel(
brightnessMirrorViewModel = kosmos.brightnessMirrorViewModel,
- shadeHeaderViewModel = shadeHeaderViewModel,
+ shadeHeaderViewModel = kosmos.shadeHeaderViewModel,
qsSceneAdapter = qsFlexiglassAdapter,
notifications = kosmos.notificationsPlaceholderViewModel,
footerActionsViewModelFactory = footerActionsViewModelFactory,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 9856f90..65fd101 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -51,6 +51,7 @@
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -69,30 +70,22 @@
import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.SceneContainerStartable
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel
-import com.android.systemui.shade.domain.interactor.privacyChipInteractor
-import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
+import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModel
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
-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.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
-import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
import com.android.systemui.testKosmos
+import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -136,9 +129,10 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper
+@EnableSceneContainer
class SceneFrameworkIntegrationTest : SysuiTestCase() {
- private val kosmos = testKosmos().apply { fakeSceneContainerFlags.enabled = true }
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val sceneContainerConfig by lazy { kosmos.sceneContainerConfig }
private val sceneInteractor by lazy { kosmos.sceneInteractor }
@@ -180,25 +174,6 @@
)
}
- private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
-
- private var mobileIconsViewModel: MobileIconsViewModel =
- MobileIconsViewModel(
- logger = mock(),
- verboseLogger = mock(),
- interactor = mobileIconsInteractor,
- airplaneModeInteractor =
- AirplaneModeInteractor(
- FakeAirplaneModeRepository(),
- FakeConnectivityRepository(),
- FakeMobileConnectionsRepository(),
- ),
- constants = mock(),
- flags = kosmos.fakeFeatureFlagsClassic,
- scope = testScope.backgroundScope,
- )
-
- private lateinit var shadeHeaderViewModel: ShadeHeaderViewModel
private lateinit var shadeSceneViewModel: ShadeSceneViewModel
private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
@@ -241,23 +216,11 @@
bouncerActionButtonInteractor = kosmos.bouncerActionButtonInteractor
bouncerViewModel = kosmos.bouncerViewModel
- shadeHeaderViewModel =
- ShadeHeaderViewModel(
- applicationScope = testScope.backgroundScope,
- context = context,
- shadeInteractor = kosmos.shadeInteractor,
- mobileIconsInteractor = mobileIconsInteractor,
- mobileIconsViewModel = mobileIconsViewModel,
- privacyChipInteractor = kosmos.privacyChipInteractor,
- clockInteractor = kosmos.shadeHeaderClockInteractor,
- broadcastDispatcher = fakeBroadcastDispatcher,
- )
-
shadeSceneViewModel =
ShadeSceneViewModel(
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = deviceEntryInteractor,
- shadeHeaderViewModel = shadeHeaderViewModel,
+ shadeHeaderViewModel = kosmos.shadeHeaderViewModel,
qsSceneAdapter = qsFlexiglassAdapter,
notifications = kosmos.notificationsPlaceholderViewModel,
brightnessMirrorViewModel = kosmos.brightnessMirrorViewModel,
@@ -266,6 +229,7 @@
footerActionsController = kosmos.footerActionsController,
footerActionsViewModelFactory = kosmos.footerActionsViewModelFactory,
sceneInteractor = sceneInteractor,
+ unfoldTransitionInteractor = kosmos.unfoldTransitionInteractor,
)
val displayTracker = FakeDisplayTracker(context)
@@ -275,15 +239,15 @@
applicationScope = testScope.backgroundScope,
sceneInteractor = sceneInteractor,
deviceEntryInteractor = deviceEntryInteractor,
+ deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor,
+ bouncerInteractor = bouncerInteractor,
keyguardInteractor = keyguardInteractor,
- flags = kosmos.fakeSceneContainerFlags,
sysUiState = sysUiState,
displayId = displayTracker.defaultDisplayId,
sceneLogger = mock(),
falsingCollector = kosmos.falsingCollector,
falsingManager = kosmos.falsingManager,
powerInteractor = powerInteractor,
- bouncerInteractor = bouncerInteractor,
simBouncerInteractor = dagger.Lazy { kosmos.simBouncerInteractor },
authenticationInteractor = dagger.Lazy { kosmos.authenticationInteractor },
windowController = mock(),
@@ -292,7 +256,7 @@
headsUpInteractor = kosmos.headsUpNotificationInteractor,
occlusionInteractor = kosmos.sceneContainerOcclusionInteractor,
faceUnlockInteractor = kosmos.deviceEntryFaceAuthInteractor,
- deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
)
startable.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index ae31058..8e2eea1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -23,10 +23,10 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -39,10 +39,10 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-@android.platform.test.annotations.EnabledOnRavenwood
+@EnableSceneContainer
class SceneContainerRepositoryTest : SysuiTestCase() {
- private val kosmos = testKosmos().apply { fakeSceneContainerFlags.enabled = true }
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index b179c30..63f4816 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -23,13 +23,13 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
@@ -45,6 +45,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
class SceneInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -55,7 +56,6 @@
@Before
fun setUp() {
- kosmos.fakeSceneContainerFlags.enabled = true
underTest = kosmos.sceneInteractor
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
index d5e43f4..bfe5ef7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
@@ -31,7 +31,6 @@
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
@@ -82,7 +81,6 @@
headsUpManager,
powerInteractor,
activeNotificationsInteractor,
- kosmos.sceneContainerFlags,
kosmos::sceneInteractor,
)
.apply { setUp(notificationPresenter, notificationsController) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 3fd5306..1472a4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -20,13 +20,11 @@
import android.app.StatusBarManager
import android.os.PowerManager
-import android.platform.test.annotations.EnableFlags
import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
-import com.android.systemui.Flags as AconfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
@@ -40,6 +38,7 @@
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
@@ -48,14 +47,16 @@
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.model.sysUiState
+import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
+import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
@@ -91,7 +92,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-@EnableFlags(AconfigFlags.FLAG_SCENE_CONTAINER)
+@EnableSceneContainer
class SceneContainerStartableTest : SysuiTestCase() {
@Mock private lateinit var windowController: NotificationShadeWindowController
@@ -100,7 +101,6 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val sceneInteractor by lazy { kosmos.sceneInteractor }
- private val sceneContainerFlags by lazy { kosmos.fakeSceneContainerFlags }
private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
private val faceAuthRepository by lazy { kosmos.fakeDeviceEntryFaceAuthRepository }
@@ -122,15 +122,15 @@
applicationScope = testScope.backgroundScope,
sceneInteractor = sceneInteractor,
deviceEntryInteractor = deviceEntryInteractor,
+ deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor,
+ bouncerInteractor = bouncerInteractor,
keyguardInteractor = keyguardInteractor,
- flags = sceneContainerFlags,
sysUiState = sysUiState,
displayId = Display.DEFAULT_DISPLAY,
sceneLogger = mock(),
falsingCollector = falsingCollector,
falsingManager = kosmos.falsingManager,
powerInteractor = powerInteractor,
- bouncerInteractor = bouncerInteractor,
simBouncerInteractor = { kosmos.simBouncerInteractor },
authenticationInteractor = { authenticationInteractor },
windowController = windowController,
@@ -139,7 +139,7 @@
headsUpInteractor = kosmos.headsUpNotificationInteractor,
occlusionInteractor = kosmos.sceneContainerOcclusionInteractor,
faceUnlockInteractor = kosmos.deviceEntryFaceAuthInteractor,
- deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
)
}
@@ -287,6 +287,38 @@
}
@Test
+ fun switchFromBouncerToQuickSettingsWhenDeviceUnlocked() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+
+ val transitionState =
+ prepareState(
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Lockscreen,
+ )
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+ runCurrent()
+
+ sceneInteractor.changeScene(Scenes.QuickSettings, "switching to qs for test")
+ transitionState.value = ObservableTransitionState.Idle(Scenes.QuickSettings)
+ runCurrent()
+ assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings)
+
+ sceneInteractor.changeScene(Scenes.Bouncer, "switching to bouncer for test")
+ transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer)
+ runCurrent()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer)
+
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+
+ assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings)
+ }
+
+ @Test
fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -1127,6 +1159,33 @@
assertThat(kosmos.fakeDeviceEntryFaceAuthRepository.isAuthRunning.value).isTrue()
}
+ @Test
+ fun switchToLockscreen_whenShadeBecomesNotTouchable() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val isShadeTouchable by collectLastValue(kosmos.shadeInteractor.isShadeTouchable)
+ val transitionStateFlow = prepareState()
+ underTest.start()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ // Flung to bouncer, 90% of the way there:
+ transitionStateFlow.value =
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Bouncer,
+ progress = flowOf(0.9f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+
+ kosmos.fakePowerRepository.updateWakefulness(WakefulnessState.ASLEEP)
+ runCurrent()
+ assertThat(isShadeTouchable).isFalse()
+
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ }
+
private fun TestScope.emulateSceneTransition(
transitionStateFlow: MutableStateFlow<ObservableTransitionState>,
toScene: SceneKey,
@@ -1166,6 +1225,7 @@
isLockscreenEnabled: Boolean = true,
startsAwake: Boolean = true,
isDeviceProvisioned: Boolean = true,
+ isInteractive: Boolean = true,
): MutableStateFlow<ObservableTransitionState> {
if (authenticationMethod?.isSecure == true) {
assert(isLockscreenEnabled) {
@@ -1182,7 +1242,6 @@
"Cannot start on the Gone scene and have the device be locked at the same time."
}
- sceneContainerFlags.enabled = true
kosmos.fakeDeviceEntryRepository.setBypassEnabled(isBypassEnabled)
authenticationMethod?.let {
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authenticationMethod)
@@ -1205,6 +1264,7 @@
} else {
powerInteractor.setAsleepForTest()
}
+ kosmos.fakePowerRepository.setInteractive(isInteractive)
kosmos.fakeDeviceProvisioningRepository.setDeviceProvisioned(isDeviceProvisioned)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagParameterizationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagParameterizationTest.kt
new file mode 100644
index 0000000..db31ad5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagParameterizationTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.shared.flag
+
+import android.platform.test.flag.junit.FlagsParameterization
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMPOSE_LOCKSCREEN
+import com.android.systemui.Flags.FLAG_EXAMPLE_FLAG
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.andSceneContainer
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+internal class SceneContainerFlagParameterizationTest : SysuiTestCase() {
+
+ @Test
+ fun emptyAndSceneContainer() {
+ val result = FlagsParameterization.allCombinationsOf().andSceneContainer()
+ Truth.assertThat(result).hasSize(2)
+ Truth.assertThat(result[0].mOverrides[FLAG_SCENE_CONTAINER]).isFalse()
+ Truth.assertThat(result[1].mOverrides[FLAG_SCENE_CONTAINER]).isTrue()
+ }
+
+ @Test
+ fun oneUnrelatedAndSceneContainer() {
+ val unrelatedFlag = FLAG_EXAMPLE_FLAG
+ val result = FlagsParameterization.allCombinationsOf(unrelatedFlag).andSceneContainer()
+ Truth.assertThat(result).hasSize(4)
+ Truth.assertThat(result[0].mOverrides[unrelatedFlag]).isFalse()
+ Truth.assertThat(result[0].mOverrides[FLAG_SCENE_CONTAINER]).isFalse()
+ Truth.assertThat(result[1].mOverrides[unrelatedFlag]).isFalse()
+ Truth.assertThat(result[1].mOverrides[FLAG_SCENE_CONTAINER]).isTrue()
+ Truth.assertThat(result[2].mOverrides[unrelatedFlag]).isTrue()
+ Truth.assertThat(result[2].mOverrides[FLAG_SCENE_CONTAINER]).isFalse()
+ Truth.assertThat(result[3].mOverrides[unrelatedFlag]).isTrue()
+ Truth.assertThat(result[3].mOverrides[FLAG_SCENE_CONTAINER]).isTrue()
+ }
+
+ @Test
+ fun oneDependencyAndSceneContainer() {
+ val dependentFlag = FLAG_COMPOSE_LOCKSCREEN
+ val result = FlagsParameterization.allCombinationsOf(dependentFlag).andSceneContainer()
+ Truth.assertThat(result).hasSize(3)
+ Truth.assertThat(result[0].mOverrides[dependentFlag]).isFalse()
+ Truth.assertThat(result[0].mOverrides[FLAG_SCENE_CONTAINER]).isFalse()
+ Truth.assertThat(result[1].mOverrides[dependentFlag]).isTrue()
+ Truth.assertThat(result[1].mOverrides[FLAG_SCENE_CONTAINER]).isFalse()
+ Truth.assertThat(result[2].mOverrides[dependentFlag]).isTrue()
+ Truth.assertThat(result[2].mOverrides[FLAG_SCENE_CONTAINER]).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
similarity index 81%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 543f6c9..ae5bf07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -16,11 +16,10 @@
package com.android.systemui.scene.shared.flag
-import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.google.common.truth.Truth
import org.junit.Test
@@ -31,16 +30,14 @@
internal class SceneContainerFlagsTest : SysuiTestCase() {
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
fun isNotEnabled_withoutAconfigFlags() {
Truth.assertThat(SceneContainerFlag.isEnabled).isEqualTo(false)
- Truth.assertThat(SceneContainerFlagsImpl().isEnabled()).isEqualTo(false)
}
@Test
@EnableSceneContainer
fun isEnabled_withAconfigFlags() {
Truth.assertThat(SceneContainerFlag.isEnabled).isEqualTo(true)
- Truth.assertThat(SceneContainerFlagsImpl().isEnabled()).isEqualTo(true)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index 7b0127e..ea95aab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -23,13 +23,13 @@
import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.classifier.fakeFalsingManager
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
@@ -45,20 +45,20 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
class SceneContainerViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope by lazy { kosmos.testScope }
private val sceneInteractor by lazy { kosmos.sceneInteractor }
- private val fakeSceneDataSource = kosmos.fakeSceneDataSource
- private val sceneContainerConfig = kosmos.sceneContainerConfig
- private val falsingManager = kosmos.fakeFalsingManager
+ private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource }
+ private val sceneContainerConfig by lazy { kosmos.sceneContainerConfig }
+ private val falsingManager by lazy { kosmos.fakeFalsingManager }
private lateinit var underTest: SceneContainerViewModel
@Before
fun setUp() {
- kosmos.fakeSceneContainerFlags.enabled = true
underTest =
SceneContainerViewModel(
sceneInteractor = sceneInteractor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
index cbbcce9..420418b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
@@ -22,6 +22,7 @@
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
@@ -30,7 +31,6 @@
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -52,6 +52,7 @@
@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
class ShadeControllerSceneImplTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val testScope = kosmos.testScope
@@ -64,7 +65,6 @@
@Before
fun setup() {
kosmos.testCase = this
- kosmos.fakeSceneContainerFlags.enabled = true
kosmos.fakeFeatureFlagsClassic.apply {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
set(Flags.NSSL_DEBUG_LINES, false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
index e759b50..26f342a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
@@ -22,9 +22,9 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shared.recents.utilities.Utilities
import com.android.systemui.testKosmos
@@ -43,8 +43,9 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
@Ignore("b/328827631")
+@EnableSceneContainer
class ShadeBackActionInteractorImplTest : SysuiTestCase() {
- val kosmos = testKosmos().apply { fakeSceneContainerFlags.enabled = true }
+ val kosmos = testKosmos()
val testScope = kosmos.testScope
val sceneInteractor = kosmos.sceneInteractor
val underTest = kosmos.shadeBackActionInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
index 757a6c9..5b33ecb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
@@ -641,7 +641,6 @@
)
)
val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
- runCurrent()
assertThat(isShadeTouchable).isFalse()
}
@@ -668,13 +667,17 @@
)
)
val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
- runCurrent()
assertThat(isShadeTouchable).isTrue()
}
@Test
fun isShadeTouchable_isFalse_whenStartingToSleepAndNotControlScreenOff() =
testScope.runTest {
+ whenever(dozeParameters.shouldControlScreenOff()).thenReturn(false)
+ val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+ // Assert the default condition is true
+ assertThat(isShadeTouchable).isTrue()
+
powerRepository.updateWakefulness(
rawState = WakefulnessState.STARTING_TO_SLEEP,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
@@ -688,15 +691,17 @@
transitionState = TransitionState.STARTED,
)
)
- whenever(dozeParameters.shouldControlScreenOff()).thenReturn(false)
- val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
- runCurrent()
assertThat(isShadeTouchable).isFalse()
}
@Test
fun isShadeTouchable_isTrue_whenStartingToSleepAndControlScreenOff() =
testScope.runTest {
+ whenever(dozeParameters.shouldControlScreenOff()).thenReturn(true)
+ val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+ // Assert the default condition is true
+ assertThat(isShadeTouchable).isTrue()
+
powerRepository.updateWakefulness(
rawState = WakefulnessState.STARTING_TO_SLEEP,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
@@ -710,9 +715,6 @@
transitionState = TransitionState.STARTED,
)
)
- whenever(dozeParameters.shouldControlScreenOff()).thenReturn(true)
- val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
- runCurrent()
assertThat(isShadeTouchable).isTrue()
}
@@ -730,7 +732,6 @@
)
)
val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
- runCurrent()
assertThat(isShadeTouchable).isTrue()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index 4c573d3..f89f18a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -2,29 +2,18 @@
import android.content.Intent
import android.provider.AlarmClock
+import android.provider.Settings
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.activityStarter
-import com.android.systemui.shade.domain.interactor.privacyChipInteractor
-import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-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.data.model.SubscriptionModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
-import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.argThat
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -40,43 +29,13 @@
class ShadeHeaderViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
+ private val mobileIconsInteractor = kosmos.fakeMobileIconsInteractor
- private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
- private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
-
- private var mobileIconsViewModel: MobileIconsViewModel =
- MobileIconsViewModel(
- logger = mock(),
- verboseLogger = mock(),
- interactor = mobileIconsInteractor,
- airplaneModeInteractor =
- AirplaneModeInteractor(
- FakeAirplaneModeRepository(),
- FakeConnectivityRepository(),
- FakeMobileConnectionsRepository(),
- ),
- constants = mock(),
- flags,
- scope = testScope.backgroundScope,
- )
-
- private lateinit var underTest: ShadeHeaderViewModel
+ private val underTest: ShadeHeaderViewModel = kosmos.shadeHeaderViewModel
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
- underTest =
- ShadeHeaderViewModel(
- applicationScope = testScope.backgroundScope,
- context = context,
- shadeInteractor = kosmos.shadeInteractor,
- mobileIconsInteractor = mobileIconsInteractor,
- mobileIconsViewModel = mobileIconsViewModel,
- privacyChipInteractor = kosmos.privacyChipInteractor,
- clockInteractor = kosmos.shadeHeaderClockInteractor,
- broadcastDispatcher = fakeBroadcastDispatcher,
- )
}
@Test
@@ -105,6 +64,19 @@
)
}
+ @Test
+ fun onShadeCarrierGroupClicked_launchesNetworkSettings() =
+ testScope.runTest {
+ val activityStarter = kosmos.activityStarter
+ underTest.onShadeCarrierGroupClicked()
+
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(
+ argThat(IntentMatcherAction(Settings.ACTION_WIRELESS_SETTINGS)),
+ anyInt(),
+ )
+ }
+
companion object {
private val SUB_1 =
SubscriptionModel(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 7a681b3..2397de6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -24,11 +24,10 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
@@ -41,24 +40,19 @@
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel
import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.domain.interactor.privacyChipInteractor
-import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.domain.startable.shadeStartable
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
-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.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
-import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.testKosmos
+import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
+import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import java.util.Locale
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -79,29 +73,8 @@
private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
private val shadeRepository by lazy { kosmos.shadeRepository }
- private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
- private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
-
- private var mobileIconsViewModel: MobileIconsViewModel =
- MobileIconsViewModel(
- logger = mock(),
- verboseLogger = mock(),
- interactor = mobileIconsInteractor,
- airplaneModeInteractor =
- AirplaneModeInteractor(
- FakeAirplaneModeRepository(),
- FakeConnectivityRepository(),
- FakeMobileConnectionsRepository(),
- ),
- constants = mock(),
- flags,
- scope = testScope.backgroundScope,
- )
-
private val qsSceneAdapter = FakeQSSceneAdapter({ mock() })
- private lateinit var shadeHeaderViewModel: ShadeHeaderViewModel
-
private lateinit var underTest: ShadeSceneViewModel
@Mock private lateinit var mediaDataManager: MediaDataManager
@@ -109,23 +82,12 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- shadeHeaderViewModel =
- ShadeHeaderViewModel(
- applicationScope = testScope.backgroundScope,
- context = context,
- shadeInteractor = kosmos.shadeInteractor,
- mobileIconsInteractor = mobileIconsInteractor,
- mobileIconsViewModel = mobileIconsViewModel,
- privacyChipInteractor = kosmos.privacyChipInteractor,
- clockInteractor = kosmos.shadeHeaderClockInteractor,
- broadcastDispatcher = fakeBroadcastDispatcher,
- )
underTest =
ShadeSceneViewModel(
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = deviceEntryInteractor,
- shadeHeaderViewModel = shadeHeaderViewModel,
+ shadeHeaderViewModel = kosmos.shadeHeaderViewModel,
qsSceneAdapter = qsSceneAdapter,
notifications = kosmos.notificationsPlaceholderViewModel,
brightnessMirrorViewModel = kosmos.brightnessMirrorViewModel,
@@ -134,6 +96,7 @@
footerActionsViewModelFactory = kosmos.footerActionsViewModelFactory,
footerActionsController = kosmos.footerActionsController,
sceneInteractor = kosmos.sceneInteractor,
+ unfoldTransitionInteractor = kosmos.unfoldTransitionInteractor,
)
}
@@ -297,4 +260,59 @@
shadeRepository.setShadeMode(ShadeMode.Split)
assertThat(shadeMode).isEqualTo(ShadeMode.Split)
}
+
+ @Test
+ fun unfoldTransitionProgress() =
+ testScope.runTest {
+ val maxTranslation = prepareConfiguration()
+ val translations by
+ collectLastValue(
+ combine(
+ underTest.unfoldTranslationX(isOnStartSide = true),
+ underTest.unfoldTranslationX(isOnStartSide = false),
+ ) { start, end ->
+ Translations(
+ start = start,
+ end = end,
+ )
+ }
+ )
+
+ val unfoldProvider = kosmos.fakeUnfoldTransitionProgressProvider
+ unfoldProvider.onTransitionStarted()
+ assertThat(translations?.start).isEqualTo(0f)
+ assertThat(translations?.end).isEqualTo(-0f)
+
+ repeat(10) { repetition ->
+ val transitionProgress = 0.1f * (repetition + 1)
+ unfoldProvider.onTransitionProgress(transitionProgress)
+ assertThat(translations?.start).isEqualTo((1 - transitionProgress) * maxTranslation)
+ assertThat(translations?.end).isEqualTo(-(1 - transitionProgress) * maxTranslation)
+ }
+
+ unfoldProvider.onTransitionFinishing()
+ assertThat(translations?.start).isEqualTo(0f)
+ assertThat(translations?.end).isEqualTo(-0f)
+
+ unfoldProvider.onTransitionFinished()
+ assertThat(translations?.start).isEqualTo(0f)
+ assertThat(translations?.end).isEqualTo(-0f)
+ }
+
+ private fun prepareConfiguration(): Int {
+ val configuration = context.resources.configuration
+ configuration.setLayoutDirection(Locale.US)
+ kosmos.fakeConfigurationRepository.onConfigurationChange(configuration)
+ val maxTranslation = 10
+ kosmos.fakeConfigurationRepository.setDimensionPixelSize(
+ R.dimen.notification_side_paddings,
+ maxTranslation
+ )
+ return maxTranslation
+ }
+
+ private data class Translations(
+ val start: Float,
+ val end: Float,
+ )
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
index a3cf929..01e1aa59 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -23,11 +23,11 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
@@ -46,11 +46,11 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- fakeSceneContainerFlags.enabled = true
fakeFeatureFlagsClassic.apply {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
set(Flags.NSSL_DEBUG_LINES, false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index ac8387f..a023033 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -20,17 +20,21 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import android.platform.test.annotations.DisableFlags
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
+import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -47,6 +51,7 @@
import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.mockLargeScreenHeaderHelper
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
@@ -64,19 +69,36 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class SharedNotificationContainerViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+// SharedNotificationContainerViewModel is only bound when FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT is on
+@EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+class SharedNotificationContainerViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(
+ FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX,
+ )
+ .andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags!!)
+ }
+
val aodBurnInViewModel = mock(AodBurnInViewModel::class.java)
lateinit var movementFlow: MutableStateFlow<BurnInModel>
val kosmos =
testKosmos().apply {
- fakeFeatureFlagsClassic.apply {
- set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
- }
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
init {
@@ -84,19 +106,28 @@
}
val testScope = kosmos.testScope
- val configurationRepository = kosmos.fakeConfigurationRepository
- val keyguardRepository = kosmos.fakeKeyguardRepository
- val keyguardInteractor = kosmos.keyguardInteractor
- val keyguardRootViewModel = kosmos.keyguardRootViewModel
- val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
- val shadeRepository = kosmos.shadeRepository
- val sharedNotificationContainerInteractor = kosmos.sharedNotificationContainerInteractor
- val largeScreenHeaderHelper = kosmos.mockLargeScreenHeaderHelper
+ val configurationRepository
+ get() = kosmos.fakeConfigurationRepository
+ val keyguardRepository
+ get() = kosmos.fakeKeyguardRepository
+ val keyguardInteractor
+ get() = kosmos.keyguardInteractor
+ val keyguardRootViewModel
+ get() = kosmos.keyguardRootViewModel
+ val keyguardTransitionRepository
+ get() = kosmos.fakeKeyguardTransitionRepository
+ val shadeRepository
+ get() = kosmos.shadeRepository
+ val sharedNotificationContainerInteractor
+ get() = kosmos.sharedNotificationContainerInteractor
+ val largeScreenHeaderHelper
+ get() = kosmos.mockLargeScreenHeaderHelper
lateinit var underTest: SharedNotificationContainerViewModel
@Before
fun setUp() {
+ assertThat(SceneContainerFlag.isEnabled).isEqualTo(SceneContainerFlag.isEnabled)
overrideResource(R.bool.config_use_split_notification_shade, false)
movementFlow = MutableStateFlow(BurnInModel())
whenever(aodBurnInViewModel.movement(any())).thenReturn(movementFlow)
@@ -130,9 +161,9 @@
}
@Test
+ @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
fun validatePaddingTopInSplitShade_refactorFlagOff_usesLargeHeaderResource() =
testScope.runTest {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
overrideResource(R.bool.config_use_split_notification_shade, true)
overrideResource(R.bool.config_use_large_screen_shade_header, true)
@@ -148,9 +179,9 @@
}
@Test
+ @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
fun validatePaddingTopInSplitShade_refactorFlagOn_usesLargeHeaderHelper() =
testScope.runTest {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
overrideResource(R.bool.config_use_split_notification_shade, true)
overrideResource(R.bool.config_use_large_screen_shade_header, true)
@@ -243,9 +274,9 @@
@Test
@DisableFlags(FLAG_SCENE_CONTAINER)
+ @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
fun validateMarginTopWithLargeScreenHeader_refactorFlagOn_usesHelper() =
testScope.runTest {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val headerResourceHeight = 50
val headerHelperHeight = 100
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
@@ -263,9 +294,9 @@
@Test
@EnableSceneContainer
+ @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
fun validateMarginTopWithLargeScreenHeader_sceneContainerFlagOn_stillZero() =
testScope.runTest {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val headerResourceHeight = 50
val headerHelperHeight = 100
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
@@ -282,6 +313,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun glanceableHubAlpha_lockscreenToHub() =
testScope.runTest {
val alpha by collectLastValue(underTest.glanceableHubAlpha)
@@ -431,6 +463,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun isOnLockscreenWithoutShade() =
testScope.runTest {
val isOnLockscreenWithoutShade by collectLastValue(underTest.isOnLockscreenWithoutShade)
@@ -467,6 +500,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun isOnGlanceableHubWithoutShade() =
testScope.runTest {
val isOnGlanceableHubWithoutShade by
@@ -503,6 +537,7 @@
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun boundsOnLockscreenNotInSplitShade() =
testScope.runTest {
val bounds by collectLastValue(underTest.bounds)
@@ -523,9 +558,9 @@
}
@Test
+ @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX, FLAG_SCENE_CONTAINER)
fun boundsOnLockscreenInSplitShade_refactorFlagOff_usesLargeHeaderResource() =
testScope.runTest {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val bounds by collectLastValue(underTest.bounds)
// When in split shade
@@ -547,13 +582,20 @@
runCurrent()
// Top should be equal to bounds (1) - padding adjustment (10)
- assertThat(bounds).isEqualTo(NotificationContainerBounds(top = -9f, bottom = 2f))
+ assertThat(bounds)
+ .isEqualTo(
+ NotificationContainerBounds(
+ top = -9f,
+ bottom = 2f,
+ )
+ )
}
@Test
+ @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun boundsOnLockscreenInSplitShade_refactorFlagOn_usesLargeHeaderHelper() =
testScope.runTest {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val bounds by collectLastValue(underTest.bounds)
// When in split shade
@@ -579,6 +621,7 @@
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun boundsOnShade() =
testScope.runTest {
val bounds by collectLastValue(underTest.bounds)
@@ -594,6 +637,7 @@
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun boundsOnQS() =
testScope.runTest {
val bounds by collectLastValue(underTest.bounds)
@@ -638,6 +682,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun maxNotificationsOnLockscreen_DoesNotUpdateWhenUserInteracting() =
testScope.runTest {
var notificationCount = 10
@@ -674,6 +719,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun maxNotificationsOnShade() =
testScope.runTest {
val calculateSpace = { space: Float, useExtraShelfSpace: Boolean -> 10 }
@@ -693,6 +739,7 @@
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun translationYUpdatesOnKeyguardForBurnIn() =
testScope.runTest {
val translationY by collectLastValue(underTest.translationY(BurnInParameters()))
@@ -726,6 +773,7 @@
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun translationYDoesNotUpdateWhenShadeIsExpanded() =
testScope.runTest {
val translationY by collectLastValue(underTest.translationY(BurnInParameters()))
@@ -746,6 +794,7 @@
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun updateBounds_fromKeyguardRoot() =
testScope.runTest {
val bounds by collectLastValue(underTest.bounds)
@@ -757,6 +806,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun alphaOnFullQsExpansion() =
testScope.runTest {
val viewState = ViewStateAccessor()
@@ -864,6 +914,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun shadeCollapseFadeIn() =
testScope.runTest {
val fadeIn by collectValues(underTest.shadeCollapseFadeIn)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
index c8062fb..1501d9c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
@@ -16,57 +16,20 @@
package com.android.systemui.statusbar.phone
-import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.Intent
-import android.os.Bundle
-import android.os.RemoteException
-import android.os.UserHandle
-import android.view.View
-import android.widget.FrameLayout
-import android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.ActivityIntentHelper
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityTransitionAnimator
-import com.android.systemui.animation.LaunchableView
-import com.android.systemui.assist.AssistManager
-import com.android.systemui.keyguard.KeyguardViewMediator
-import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.shade.ShadeController
-import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.shade.data.repository.ShadeAnimationRepository
-import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
-import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
-import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
-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.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.nullable
-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 java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -74,177 +37,21 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class ActivityStarterImplTest : SysuiTestCase() {
- @Mock private lateinit var centralSurfaces: CentralSurfaces
- @Mock private lateinit var assistManager: AssistManager
- @Mock private lateinit var dozeServiceHost: DozeServiceHost
- @Mock private lateinit var biometricUnlockController: BiometricUnlockController
- @Mock private lateinit var keyguardViewMediator: KeyguardViewMediator
- @Mock private lateinit var shadeController: ShadeController
- @Mock private lateinit var commandQueue: CommandQueue
- @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
- @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator
- @Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager
- @Mock private lateinit var statusBarWindowController: StatusBarWindowController
- @Mock private lateinit var notifShadeWindowController: NotificationShadeWindowController
- @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
- @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var legacyActivityStarterInternal: LegacyActivityStarterInternalImpl
+ @Mock private lateinit var activityStarterInternal: ActivityStarterInternalImpl
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
- @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
- @Mock private lateinit var userTracker: UserTracker
- @Mock private lateinit var activityIntentHelper: ActivityIntentHelper
private lateinit var underTest: ActivityStarterImpl
private val mainExecutor = FakeExecutor(FakeSystemClock())
- private val shadeAnimationInteractor =
- ShadeAnimationInteractorLegacyImpl(ShadeAnimationRepository(), FakeShadeRepository())
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
underTest =
ActivityStarterImpl(
- Lazy { Optional.of(centralSurfaces) },
- Lazy { assistManager },
- Lazy { dozeServiceHost },
- Lazy { biometricUnlockController },
- Lazy { keyguardViewMediator },
- Lazy { shadeController },
- commandQueue,
- shadeAnimationInteractor,
- Lazy { statusBarKeyguardViewManager },
- Lazy { notifShadeWindowController },
- mActivityTransitionAnimator,
- context,
- DISPLAY_ID,
- lockScreenUserManager,
- statusBarWindowController,
- wakefulnessLifecycle,
- keyguardStateController,
- statusBarStateController,
- keyguardUpdateMonitor,
- deviceProvisionedController,
- userTracker,
- activityIntentHelper,
- mainExecutor,
+ statusBarStateController = statusBarStateController,
+ mainExecutor = mainExecutor,
+ legacyActivityStarter = { legacyActivityStarterInternal },
)
- whenever(userTracker.userHandle).thenReturn(UserHandle.OWNER)
- }
-
- @Test
- fun startPendingIntentDismissingKeyguard_keyguardShowing_dismissWithAction() {
- val pendingIntent = mock(PendingIntent::class.java)
- whenever(pendingIntent.isActivity).thenReturn(true)
- whenever(keyguardStateController.isShowing).thenReturn(true)
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
-
- underTest.startPendingIntentDismissingKeyguard(pendingIntent)
- mainExecutor.runAllReady()
-
- verify(statusBarKeyguardViewManager)
- .dismissWithAction(any(OnDismissAction::class.java), eq(null), anyBoolean(), eq(null))
- }
-
- @Test
- fun startPendingIntentMaybeDismissingKeyguard_keyguardShowing_showOverLs_launchAnimator() {
- val pendingIntent = mock(PendingIntent::class.java)
- val parent = FrameLayout(context)
- val view =
- object : View(context), LaunchableView {
- override fun setShouldBlockVisibilityChanges(block: Boolean) {}
- }
- parent.addView(view)
- val controller = ActivityTransitionAnimator.Controller.fromView(view)
- whenever(pendingIntent.isActivity).thenReturn(true)
- whenever(keyguardStateController.isShowing).thenReturn(true)
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
- whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
- .thenReturn(true)
-
- underTest.startPendingIntentMaybeDismissingKeyguard(
- intent = pendingIntent,
- animationController = controller,
- intentSentUiThreadCallback = null,
- )
- mainExecutor.runAllReady()
-
- verify(mActivityTransitionAnimator)
- .startPendingIntentWithAnimation(
- nullable(),
- eq(true),
- nullable(),
- eq(true),
- any(),
- )
- }
-
- fun startPendingIntentDismissingKeyguard_fillInIntentAndExtraOptions_sendAndReturnResult() {
- val pendingIntent = mock(PendingIntent::class.java)
- val fillInIntent = mock(Intent::class.java)
- val parent = FrameLayout(context)
- val view =
- object : View(context), LaunchableView {
- override fun setShouldBlockVisibilityChanges(block: Boolean) {}
- }
- parent.addView(view)
- val controller = ActivityTransitionAnimator.Controller.fromView(view)
- whenever(pendingIntent.isActivity).thenReturn(true)
- whenever(keyguardStateController.isShowing).thenReturn(true)
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
- whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
- .thenReturn(false)
-
- // extra activity options to set on pending intent
- val activityOptions = mock(ActivityOptions::class.java)
- activityOptions.splashScreenStyle = SPLASH_SCREEN_STYLE_SOLID_COLOR
- activityOptions.isPendingIntentBackgroundActivityLaunchAllowedByPermission = false
- val bundleCaptor = argumentCaptor<Bundle>()
-
- underTest.startPendingIntentMaybeDismissingKeyguard(
- intent = pendingIntent,
- animationController = controller,
- intentSentUiThreadCallback = null,
- fillInIntent = fillInIntent,
- extraOptions = activityOptions.toBundle(),
- )
- mainExecutor.runAllReady()
-
- // Fill-in intent is passed and options contain extra values specified
- verify(pendingIntent)
- .sendAndReturnResult(
- eq(context),
- eq(0),
- eq(fillInIntent),
- nullable(),
- nullable(),
- nullable(),
- bundleCaptor.capture()
- )
- val options = ActivityOptions.fromBundle(bundleCaptor.value)
- assertThat(options.isPendingIntentBackgroundActivityLaunchAllowedByPermission).isFalse()
- assertThat(options.splashScreenStyle).isEqualTo(SPLASH_SCREEN_STYLE_SOLID_COLOR)
- }
-
- @Test
- fun startPendingIntentDismissingKeyguard_associatedView_getAnimatorController() {
- val pendingIntent = mock(PendingIntent::class.java)
- val associatedView = mock(ExpandableNotificationRow::class.java)
-
- underTest.startPendingIntentDismissingKeyguard(
- intent = pendingIntent,
- intentSentUiThreadCallback = null,
- associatedView = associatedView,
- )
-
- verify(centralSurfaces).getAnimatorControllerFromNotification(associatedView)
- }
-
- @Test
- fun startActivity_noUserHandleProvided_getUserHandle() {
- val intent = mock(Intent::class.java)
-
- underTest.startActivity(intent, false)
-
- verify(userTracker).userHandle
}
@Test
@@ -258,115 +65,9 @@
@Test
fun postStartActivityDismissingKeyguard_intent_postsOnMain() {
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
- val intent = mock(Intent::class.java)
-
- underTest.postStartActivityDismissingKeyguard(intent, 0)
+ underTest.postStartActivityDismissingKeyguard(mock(Intent::class.java), 0)
assertThat(mainExecutor.numPending()).isEqualTo(1)
- mainExecutor.runAllReady()
-
- verify(deviceProvisionedController).isDeviceProvisioned
- verify(shadeController).collapseShadeForActivityStart()
- }
-
- @Test
- fun postStartActivityDismissingKeyguard_intent_notDeviceProvisioned_doesNotProceed() {
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
- val intent = mock(Intent::class.java)
-
- underTest.postStartActivityDismissingKeyguard(intent, 0)
- mainExecutor.runAllReady()
-
- verify(deviceProvisionedController).isDeviceProvisioned
- verify(shadeController, never()).collapseShadeForActivityStart()
- }
-
- @Test
- fun dismissKeyguardThenExecute_startWakeAndUnlock() {
- whenever(wakefulnessLifecycle.wakefulness)
- .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
- whenever(keyguardStateController.canDismissLockScreen()).thenReturn(true)
- whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
- whenever(dozeServiceHost.isPulsing).thenReturn(true)
-
- underTest.dismissKeyguardThenExecute({ true }, {}, false)
-
- verify(biometricUnlockController)
- .startWakeAndUnlock(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
- }
-
- @Test
- fun dismissKeyguardThenExecute_keyguardIsShowing_dismissWithAction() {
- val customMessage = "Enter your pin."
- whenever(keyguardStateController.isShowing).thenReturn(true)
-
- underTest.dismissKeyguardThenExecute({ true }, {}, false, customMessage)
-
- verify(statusBarKeyguardViewManager)
- .dismissWithAction(
- any(OnDismissAction::class.java),
- any(Runnable::class.java),
- eq(false),
- eq(customMessage)
- )
- }
-
- @Test
- fun dismissKeyguardThenExecute_awakeDreams() {
- val customMessage = "Enter your pin."
- var dismissActionExecuted = false
- whenever(keyguardStateController.isShowing).thenReturn(false)
- whenever(keyguardUpdateMonitor.isDreaming).thenReturn(true)
-
- underTest.dismissKeyguardThenExecute(
- {
- dismissActionExecuted = true
- true
- },
- {},
- false,
- customMessage
- )
-
- verify(centralSurfaces).awakenDreams()
- assertThat(dismissActionExecuted).isTrue()
- }
-
- @Test
- @Throws(RemoteException::class)
- fun executeRunnableDismissingKeyguard_dreaming_notShowing_awakenDreams() {
- whenever(keyguardStateController.isShowing).thenReturn(false)
- whenever(keyguardStateController.isOccluded).thenReturn(false)
- whenever(keyguardUpdateMonitor.isDreaming).thenReturn(true)
-
- underTest.executeRunnableDismissingKeyguard(
- runnable = {},
- cancelAction = null,
- dismissShade = false,
- afterKeyguardGone = false,
- deferred = false
- )
-
- verify(centralSurfaces, times(1)).awakenDreams()
- }
-
- @Test
- @Throws(RemoteException::class)
- fun executeRunnableDismissingKeyguard_notDreaming_notShowing_doNotAwakenDreams() {
- whenever(keyguardStateController.isShowing).thenReturn(false)
- whenever(keyguardStateController.isOccluded).thenReturn(false)
- whenever(keyguardUpdateMonitor.isDreaming).thenReturn(false)
-
- underTest.executeRunnableDismissingKeyguard(
- runnable = {},
- cancelAction = null,
- dismissShade = false,
- afterKeyguardGone = false,
- deferred = false
- )
-
- verify(centralSurfaces, never()).awakenDreams()
}
@Test
@@ -377,8 +78,4 @@
mainExecutor.runAllReady()
verify(statusBarStateController).setLeaveOpenOnKeyguardHide(true)
}
-
- private companion object {
- private const val DISPLAY_ID = 0
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
new file mode 100644
index 0000000..b443489
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.app.ActivityOptions
+import android.app.PendingIntent
+import android.content.Intent
+import android.os.Bundle
+import android.os.RemoteException
+import android.os.UserHandle
+import android.view.View
+import android.widget.FrameLayout
+import android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.LaunchableView
+import com.android.systemui.assist.AssistManager
+import com.android.systemui.keyguard.KeyguardViewMediator
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeController
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.data.repository.ShadeAnimationRepository
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+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.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
+ @Mock private lateinit var centralSurfaces: CentralSurfaces
+ @Mock private lateinit var assistManager: AssistManager
+ @Mock private lateinit var dozeServiceHost: DozeServiceHost
+ @Mock private lateinit var biometricUnlockController: BiometricUnlockController
+ @Mock private lateinit var keyguardViewMediator: KeyguardViewMediator
+ @Mock private lateinit var shadeController: ShadeController
+ @Mock private lateinit var commandQueue: CommandQueue
+ @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock private lateinit var activityTransitionAnimator: ActivityTransitionAnimator
+ @Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager
+ @Mock private lateinit var statusBarWindowController: StatusBarWindowController
+ @Mock private lateinit var notifShadeWindowController: NotificationShadeWindowController
+ @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var activityIntentHelper: ActivityIntentHelper
+ private lateinit var underTest: LegacyActivityStarterInternalImpl
+ private val mainExecutor = FakeExecutor(FakeSystemClock())
+ private val shadeAnimationInteractor =
+ ShadeAnimationInteractorLegacyImpl(ShadeAnimationRepository(), FakeShadeRepository())
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ LegacyActivityStarterInternalImpl(
+ centralSurfacesOptLazy = { Optional.of(centralSurfaces) },
+ assistManagerLazy = { assistManager },
+ dozeServiceHostLazy = { dozeServiceHost },
+ biometricUnlockControllerLazy = { biometricUnlockController },
+ keyguardViewMediatorLazy = { keyguardViewMediator },
+ shadeControllerLazy = { shadeController },
+ commandQueue = commandQueue,
+ shadeAnimationInteractor = shadeAnimationInteractor,
+ statusBarKeyguardViewManagerLazy = { statusBarKeyguardViewManager },
+ notifShadeWindowControllerLazy = { notifShadeWindowController },
+ activityTransitionAnimator = activityTransitionAnimator,
+ context = context,
+ displayId = DISPLAY_ID,
+ lockScreenUserManager = lockScreenUserManager,
+ statusBarWindowController = statusBarWindowController,
+ wakefulnessLifecycle = wakefulnessLifecycle,
+ keyguardStateController = keyguardStateController,
+ statusBarStateController = statusBarStateController,
+ keyguardUpdateMonitor = keyguardUpdateMonitor,
+ deviceProvisionedController = deviceProvisionedController,
+ userTracker = userTracker,
+ activityIntentHelper = activityIntentHelper,
+ mainExecutor = mainExecutor,
+ )
+ whenever(userTracker.userHandle).thenReturn(UserHandle.OWNER)
+ }
+
+ @Test
+ fun startPendingIntentDismissingKeyguard_keyguardShowing_dismissWithAction() {
+ val pendingIntent = mock(PendingIntent::class.java)
+ whenever(pendingIntent.isActivity).thenReturn(true)
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+
+ underTest.startPendingIntentDismissingKeyguard(pendingIntent)
+ mainExecutor.runAllReady()
+
+ verify(statusBarKeyguardViewManager)
+ .dismissWithAction(any(OnDismissAction::class.java), eq(null), anyBoolean(), eq(null))
+ }
+
+ @Test
+ fun startPendingIntentMaybeDismissingKeyguard_keyguardShowing_showOverLs_launchAnimator() {
+ val pendingIntent = mock(PendingIntent::class.java)
+ val parent = FrameLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
+ whenever(pendingIntent.isActivity).thenReturn(true)
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
+ .thenReturn(true)
+
+ startPendingIntentMaybeDismissingKeyguard(
+ intent = pendingIntent,
+ animationController = controller,
+ intentSentUiThreadCallback = null,
+ )
+ mainExecutor.runAllReady()
+
+ verify(activityTransitionAnimator)
+ .startPendingIntentWithAnimation(
+ nullable(),
+ eq(true),
+ nullable(),
+ eq(true),
+ any(),
+ )
+ }
+
+ fun startPendingIntentDismissingKeyguard_fillInIntentAndExtraOptions_sendAndReturnResult() {
+ val pendingIntent = mock(PendingIntent::class.java)
+ val fillInIntent = mock(Intent::class.java)
+ val parent = FrameLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
+ whenever(pendingIntent.isActivity).thenReturn(true)
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
+ .thenReturn(false)
+
+ // extra activity options to set on pending intent
+ val activityOptions = mock(ActivityOptions::class.java)
+ activityOptions.splashScreenStyle = SPLASH_SCREEN_STYLE_SOLID_COLOR
+ activityOptions.isPendingIntentBackgroundActivityLaunchAllowedByPermission = false
+ val bundleCaptor = argumentCaptor<Bundle>()
+
+ startPendingIntentMaybeDismissingKeyguard(
+ intent = pendingIntent,
+ animationController = controller,
+ intentSentUiThreadCallback = null,
+ fillInIntent = fillInIntent,
+ extraOptions = activityOptions.toBundle(),
+ )
+ mainExecutor.runAllReady()
+
+ // Fill-in intent is passed and options contain extra values specified
+ verify(pendingIntent)
+ .sendAndReturnResult(
+ eq(context),
+ eq(0),
+ eq(fillInIntent),
+ nullable(),
+ nullable(),
+ nullable(),
+ bundleCaptor.capture()
+ )
+ val options = ActivityOptions.fromBundle(bundleCaptor.value)
+ assertThat(options.isPendingIntentBackgroundActivityLaunchAllowedByPermission).isFalse()
+ assertThat(options.splashScreenStyle).isEqualTo(SPLASH_SCREEN_STYLE_SOLID_COLOR)
+ }
+
+ @Test
+ fun startPendingIntentDismissingKeyguard_associatedView_getAnimatorController() {
+ val pendingIntent = mock(PendingIntent::class.java)
+ val associatedView = mock(ExpandableNotificationRow::class.java)
+
+ underTest.startPendingIntentDismissingKeyguard(
+ intent = pendingIntent,
+ intentSentUiThreadCallback = null,
+ associatedView = associatedView,
+ )
+
+ verify(centralSurfaces).getAnimatorControllerFromNotification(associatedView)
+ }
+
+ @Test
+ fun startActivity_noUserHandleProvided_getUserHandle() {
+ val intent = mock(Intent::class.java)
+
+ underTest.startActivity(intent, false, null, false, null)
+
+ verify(userTracker).userHandle
+ }
+
+ @Test
+ fun dismissKeyguardThenExecute_startWakeAndUnlock() {
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
+ whenever(keyguardStateController.canDismissLockScreen()).thenReturn(true)
+ whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
+ whenever(dozeServiceHost.isPulsing).thenReturn(true)
+
+ underTest.dismissKeyguardThenExecute({ true }, {}, false)
+
+ verify(biometricUnlockController)
+ .startWakeAndUnlock(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
+ }
+
+ @Test
+ fun dismissKeyguardThenExecute_keyguardIsShowing_dismissWithAction() {
+ val customMessage = "Enter your pin."
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+
+ underTest.dismissKeyguardThenExecute({ true }, {}, false, customMessage)
+
+ verify(statusBarKeyguardViewManager)
+ .dismissWithAction(
+ any(OnDismissAction::class.java),
+ any(Runnable::class.java),
+ eq(false),
+ eq(customMessage)
+ )
+ }
+
+ @Test
+ fun dismissKeyguardThenExecute_awakeDreams() {
+ val customMessage = "Enter your pin."
+ var dismissActionExecuted = false
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+ whenever(keyguardUpdateMonitor.isDreaming).thenReturn(true)
+
+ underTest.dismissKeyguardThenExecute(
+ {
+ dismissActionExecuted = true
+ true
+ },
+ {},
+ false,
+ customMessage
+ )
+
+ verify(centralSurfaces).awakenDreams()
+ assertThat(dismissActionExecuted).isTrue()
+ }
+
+ @Test
+ @Throws(RemoteException::class)
+ fun executeRunnableDismissingKeyguard_dreaming_notShowing_awakenDreams() {
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+ whenever(keyguardStateController.isOccluded).thenReturn(false)
+ whenever(keyguardUpdateMonitor.isDreaming).thenReturn(true)
+
+ underTest.executeRunnableDismissingKeyguard(
+ runnable = {},
+ cancelAction = null,
+ dismissShade = false,
+ afterKeyguardGone = false,
+ deferred = false
+ )
+
+ verify(centralSurfaces, times(1)).awakenDreams()
+ }
+
+ @Test
+ @Throws(RemoteException::class)
+ fun executeRunnableDismissingKeyguard_notDreaming_notShowing_doNotAwakenDreams() {
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+ whenever(keyguardStateController.isOccluded).thenReturn(false)
+ whenever(keyguardUpdateMonitor.isDreaming).thenReturn(false)
+
+ underTest.executeRunnableDismissingKeyguard(
+ runnable = {},
+ cancelAction = null,
+ dismissShade = false,
+ afterKeyguardGone = false,
+ deferred = false
+ )
+
+ verify(centralSurfaces, never()).awakenDreams()
+ }
+
+ private fun startPendingIntentMaybeDismissingKeyguard(
+ intent: PendingIntent,
+ intentSentUiThreadCallback: Runnable?,
+ animationController: ActivityTransitionAnimator.Controller?,
+ fillInIntent: Intent? = null,
+ extraOptions: Bundle? = null,
+ ) {
+ underTest.startPendingIntentDismissingKeyguard(
+ intent = intent,
+ intentSentUiThreadCallback = intentSentUiThreadCallback,
+ animationController = animationController,
+ showOverLockscreen = true,
+ fillInIntent = fillInIntent,
+ extraOptions = extraOptions,
+ )
+ }
+
+ private companion object {
+ private const val DISPLAY_ID = 0
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
index 3c9dc63..69207ba 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
@@ -89,7 +89,7 @@
}
@Override
- public boolean isHeadsUpGoingAway() {
+ public boolean isHeadsUpAnimatingAwayValue() {
throw new UnsupportedOperationException();
}
@@ -115,7 +115,7 @@
}
@Override
- public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+ public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
throw new UnsupportedOperationException();
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt
new file mode 100644
index 0000000..12f08a3
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt
@@ -0,0 +1,184 @@
+/*
+ * 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.unfold.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
+import com.google.common.truth.Truth.assertThat
+import java.util.Locale
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UnfoldTransitionInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val unfoldTransitionProgressProvider = kosmos.fakeUnfoldTransitionProgressProvider
+
+ private val underTest: UnfoldTransitionInteractor = kosmos.unfoldTransitionInteractor
+
+ @Test
+ fun waitForTransitionFinish_noEvents_doesNotComplete() =
+ testScope.runTest {
+ val deferred = async { underTest.waitForTransitionFinish() }
+
+ runCurrent()
+
+ assertThat(deferred.isCompleted).isFalse()
+ deferred.cancel()
+ }
+
+ @Test
+ fun waitForTransitionFinish_finishEvent_completes() =
+ testScope.runTest {
+ val deferred = async { underTest.waitForTransitionFinish() }
+
+ runCurrent()
+ unfoldTransitionProgressProvider.onTransitionFinished()
+ runCurrent()
+
+ assertThat(deferred.isCompleted).isTrue()
+ deferred.cancel()
+ }
+
+ @Test
+ fun waitForTransitionFinish_otherEvent_doesNotComplete() =
+ testScope.runTest {
+ val deferred = async { underTest.waitForTransitionFinish() }
+
+ runCurrent()
+ unfoldTransitionProgressProvider.onTransitionStarted()
+ runCurrent()
+
+ assertThat(deferred.isCompleted).isFalse()
+ deferred.cancel()
+ }
+
+ @Test
+ fun unfoldTranslationX_leftToRight() =
+ testScope.runTest {
+ val maxTranslation = prepareConfiguration(isLeftToRight = true)
+ val translations by
+ collectLastValue(
+ combine(
+ underTest.unfoldTranslationX(isOnStartSide = true),
+ underTest.unfoldTranslationX(isOnStartSide = false),
+ ) { start, end ->
+ Translations(
+ start = start,
+ end = end,
+ )
+ }
+ )
+ runCurrent()
+
+ unfoldTransitionProgressProvider.onTransitionStarted()
+ assertThat(translations?.start).isEqualTo(0f)
+ assertThat(translations?.end).isEqualTo(-0f)
+
+ repeat(10) { repetition ->
+ val transitionProgress = 1 - 0.1f * (repetition + 1)
+ unfoldTransitionProgressProvider.onTransitionProgress(transitionProgress)
+ assertThat(translations?.start).isEqualTo((1 - transitionProgress) * maxTranslation)
+ assertThat(translations?.end).isEqualTo(-(1 - transitionProgress) * maxTranslation)
+ }
+
+ unfoldTransitionProgressProvider.onTransitionFinishing()
+ assertThat(translations?.start).isEqualTo(maxTranslation)
+ assertThat(translations?.end).isEqualTo(-maxTranslation)
+
+ unfoldTransitionProgressProvider.onTransitionFinished()
+ assertThat(translations?.start).isEqualTo(0f)
+ assertThat(translations?.end).isEqualTo(-0f)
+ }
+
+ @Test
+ fun unfoldTranslationX_rightToLeft() =
+ testScope.runTest {
+ val maxTranslation = prepareConfiguration(isLeftToRight = false)
+ val translations by
+ collectLastValue(
+ combine(
+ underTest.unfoldTranslationX(isOnStartSide = true),
+ underTest.unfoldTranslationX(isOnStartSide = false),
+ ) { start, end ->
+ Translations(
+ start = start,
+ end = end,
+ )
+ }
+ )
+ runCurrent()
+
+ unfoldTransitionProgressProvider.onTransitionStarted()
+ assertThat(translations?.start).isEqualTo(-0f)
+ assertThat(translations?.end).isEqualTo(0f)
+
+ repeat(10) { repetition ->
+ val transitionProgress = 1 - 0.1f * (repetition + 1)
+ unfoldTransitionProgressProvider.onTransitionProgress(transitionProgress)
+ assertThat(translations?.start)
+ .isEqualTo(-(1 - transitionProgress) * maxTranslation)
+ assertThat(translations?.end).isEqualTo((1 - transitionProgress) * maxTranslation)
+ }
+
+ unfoldTransitionProgressProvider.onTransitionFinishing()
+ assertThat(translations?.start).isEqualTo(-maxTranslation)
+ assertThat(translations?.end).isEqualTo(maxTranslation)
+
+ unfoldTransitionProgressProvider.onTransitionFinished()
+ assertThat(translations?.start).isEqualTo(-0f)
+ assertThat(translations?.end).isEqualTo(0f)
+ }
+
+ private fun prepareConfiguration(
+ isLeftToRight: Boolean,
+ ): Int {
+ val configuration = context.resources.configuration
+ if (isLeftToRight) {
+ configuration.setLayoutDirection(Locale.US)
+ } else {
+ configuration.setLayoutDirection(Locale("he", "il"))
+ }
+ kosmos.fakeConfigurationRepository.onConfigurationChange(configuration)
+ val maxTranslation = 10
+ kosmos.fakeConfigurationRepository.setDimensionPixelSize(
+ R.dimen.notification_side_paddings,
+ maxTranslation
+ )
+ return maxTranslation
+ }
+
+ private data class Translations(
+ val start: Float,
+ val end: Float,
+ )
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartableTest.kt
new file mode 100644
index 0000000..8bb3672
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartableTest.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.domain.startable
+
+import android.media.AudioManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLogger
+import com.android.internal.logging.uiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.audioModeInteractor
+import com.android.systemui.volume.audioRepository
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class AudioModeLoggerStartableTest : SysuiTestCase() {
+ @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AudioModeLoggerStartable
+
+ @Before
+ fun setUp() {
+ with(kosmos) {
+ underTest =
+ AudioModeLoggerStartable(
+ applicationCoroutineScope,
+ uiEventLogger,
+ audioModeInteractor
+ )
+ }
+ }
+
+ @Test
+ fun audioMode_inCall() {
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setMode(AudioManager.MODE_IN_CALL)
+
+ underTest.start()
+ runCurrent()
+
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(VolumePanelUiEvent.VOLUME_PANEL_AUDIO_MODE_CHANGE_TO_CALLING.id)
+ }
+ }
+ }
+
+ @Test
+ fun audioMode_notInCall() {
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setMode(AudioManager.MODE_NORMAL)
+
+ underTest.start()
+ runCurrent()
+
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(VolumePanelUiEvent.VOLUME_PANEL_AUDIO_MODE_CHANGE_TO_NORMAL.id)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
index e31cdcd..dc96139 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
@@ -72,7 +72,7 @@
testScope.runTest {
localMediaRepository.updateCurrentConnectedDevice(null)
- val slice by collectLastValue(underTest.ancSlice(1))
+ val slice by collectLastValue(underTest.ancSlice(1, false, false))
runCurrent()
assertThat(slice).isNull()
@@ -86,7 +86,7 @@
testScope.runTest {
localMediaRepository.updateCurrentConnectedDevice(createMediaDevice())
- val slice by collectLastValue(underTest.ancSlice(1))
+ val slice by collectLastValue(underTest.ancSlice(1, false, false))
runCurrent()
assertThat(slice).isNotNull()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
index 53f0bc9..81e6ac4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
@@ -24,6 +24,7 @@
import com.android.systemui.testKosmos
import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
import com.android.systemui.volume.panel.component.anc.ancSliceRepository
+import com.android.systemui.volume.panel.component.anc.domain.model.AncSlices
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -57,10 +58,10 @@
FakeSliceFactory.createSlice(hasError = true, hasSliceItem = true)
)
- val slice by collectLastValue(underTest.ancSlice)
+ val slice by collectLastValue(underTest.ancSlices)
runCurrent()
- assertThat(slice).isNull()
+ assertThat(slice).isInstanceOf(AncSlices.Unavailable::class.java)
}
}
}
@@ -74,10 +75,10 @@
FakeSliceFactory.createSlice(hasError = false, hasSliceItem = false)
)
- val slice by collectLastValue(underTest.ancSlice)
+ val slice by collectLastValue(underTest.ancSlices)
runCurrent()
- assertThat(slice).isNull()
+ assertThat(slice).isInstanceOf(AncSlices.Unavailable::class.java)
}
}
}
@@ -91,10 +92,10 @@
FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
)
- val slice by collectLastValue(underTest.ancSlice)
+ val slice by collectLastValue(underTest.ancSlices)
runCurrent()
- assertThat(slice).isNotNull()
+ assertThat(slice).isInstanceOf(AncSlices.Ready::class.java)
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt
index 2cc1ad3..27a813f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt
@@ -21,6 +21,8 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLogger
+import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -29,6 +31,7 @@
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import com.android.systemui.volume.panel.volumePanelViewModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -58,7 +61,10 @@
private lateinit var underTest: BottomBarViewModel
private fun initUnderTest() {
- underTest = with(kosmos) { BottomBarViewModel(activityStarter, volumePanelViewModel) }
+ underTest =
+ with(kosmos) {
+ BottomBarViewModel(activityStarter, volumePanelViewModel, uiEventLogger)
+ }
}
@Test
@@ -96,6 +102,8 @@
/* userHandle = */ eq(null),
)
assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_SOUND_SETTINGS)
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(VolumePanelUiEvent.VOLUME_PANEL_SOUND_SETTINGS_CLICKED.id)
activityStartedCaptor.value.onActivityStarted(ActivityManager.START_SUCCESS)
val volumePanelState by collectLastValue(volumePanelViewModel.volumePanelState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
index 610195f..4cf924a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
@@ -18,6 +18,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -45,7 +46,12 @@
fun setup() {
underTest =
with(kosmos) {
- CaptioningViewModel(context, captioningInteractor, testScope.backgroundScope)
+ CaptioningViewModel(
+ context,
+ captioningInteractor,
+ testScope.backgroundScope,
+ uiEventLogger,
+ )
}
}
@@ -58,7 +64,7 @@
val buttonViewModel by collectLastValue(underTest.buttonViewModel)
runCurrent()
- assertThat(buttonViewModel!!.isChecked).isFalse()
+ assertThat(buttonViewModel!!.isActive).isFalse()
}
}
}
@@ -72,7 +78,7 @@
val buttonViewModel by collectLastValue(underTest.buttonViewModel)
runCurrent()
- assertThat(buttonViewModel!!.isChecked).isTrue()
+ assertThat(buttonViewModel!!.isActive).isTrue()
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
index ec55c75..da0a229 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
@@ -46,7 +46,10 @@
@Before
fun setup() {
- underTest = MediaOutputAvailabilityCriteria(kosmos.audioModeInteractor)
+ underTest =
+ MediaOutputAvailabilityCriteria(
+ kosmos.audioModeInteractor,
+ )
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
index 462f36d..30524d9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
@@ -22,6 +22,7 @@
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -64,6 +65,7 @@
mediaOutputActionsInteractor,
mediaDeviceSessionInteractor,
mediaOutputInteractor,
+ uiEventLogger,
)
with(context.orCreateTestableResources) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt
deleted file mode 100644
index 79d3fe9..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume.panel.component.volume.domain.interactor
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class VolumeSliderInteractorTest : SysuiTestCase() {
-
- private val underTest = VolumeSliderInteractor()
-
- @Test
- fun processVolumeToValue_returnsTranslatedVolume() {
- assertThat(underTest.processVolumeToValue(2, volumeRange)).isEqualTo(20f)
- }
-
- private companion object {
- val volumeRange = 0..10
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/WMShellTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/WMShellTest.kt
new file mode 100644
index 0000000..55e46dc
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/WMShellTest.kt
@@ -0,0 +1,215 @@
+/*
+ * 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.systemui.wmshell
+
+import android.content.pm.UserInfo
+import android.graphics.Color
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.keyguardUpdateMonitor
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel
+import com.android.systemui.communal.util.fakeCommunalColors
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.dock.DockManager
+import com.android.systemui.dock.fakeDockManager
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.wakefulnessLifecycle
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.model.SysUiState
+import com.android.systemui.model.sysUiState
+import com.android.systemui.notetask.NoteTaskInitializer
+import com.android.systemui.settings.FakeDisplayTracker
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.settings.userTracker
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.commandQueue
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.configurationController
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.wm.shell.desktopmode.DesktopMode
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
+import com.android.wm.shell.onehanded.OneHanded
+import com.android.wm.shell.onehanded.OneHandedEventCallback
+import com.android.wm.shell.onehanded.OneHandedTransitionCallback
+import com.android.wm.shell.pip.Pip
+import com.android.wm.shell.recents.RecentTasks
+import com.android.wm.shell.splitscreen.SplitScreen
+import com.android.wm.shell.sysui.ShellInterface
+import java.util.Optional
+import java.util.concurrent.Executor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+/**
+ * Tests for [WMShell].
+ *
+ * Build/Install/Run: atest SystemUITests:WMShellTest
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class WMShellTest : SysuiTestCase() {
+ val kosmos = testKosmos()
+ val testScope = kosmos.testScope
+
+ @Mock private lateinit var mShellInterface: ShellInterface
+ @Mock private lateinit var mScreenLifecycle: ScreenLifecycle
+ @Mock private lateinit var mPip: Pip
+ @Mock private lateinit var mSplitScreen: SplitScreen
+ @Mock private lateinit var mOneHanded: OneHanded
+ @Mock private lateinit var mNoteTaskInitializer: NoteTaskInitializer
+ @Mock private lateinit var mDesktopMode: DesktopMode
+ @Mock private lateinit var mRecentTasks: RecentTasks
+
+ private val mCommandQueue: CommandQueue = kosmos.commandQueue
+ private val mConfigurationController: ConfigurationController = kosmos.configurationController
+ private val mKeyguardStateController: KeyguardStateController = kosmos.keyguardStateController
+ private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor = kosmos.keyguardUpdateMonitor
+ private val mSysUiState: SysUiState = kosmos.sysUiState
+ private val mWakefulnessLifecycle: WakefulnessLifecycle = kosmos.wakefulnessLifecycle
+ private val mUserTracker: UserTracker = kosmos.userTracker
+ private val mSysUiMainExecutor: Executor = kosmos.fakeExecutor
+ private val communalTransitionViewModel = kosmos.communalTransitionViewModel
+
+ private lateinit var underTest: WMShell
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ val displayTracker = FakeDisplayTracker(mContext)
+
+ kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO))
+ kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+
+ underTest =
+ WMShell(
+ mContext,
+ mShellInterface,
+ Optional.of(mPip),
+ Optional.of(mSplitScreen),
+ Optional.of(mOneHanded),
+ Optional.of(mDesktopMode),
+ Optional.of(mRecentTasks),
+ mCommandQueue,
+ mConfigurationController,
+ mKeyguardStateController,
+ mKeyguardUpdateMonitor,
+ mScreenLifecycle,
+ mSysUiState,
+ mWakefulnessLifecycle,
+ mUserTracker,
+ displayTracker,
+ mNoteTaskInitializer,
+ communalTransitionViewModel,
+ JavaAdapter(testScope.backgroundScope),
+ mSysUiMainExecutor
+ )
+ }
+
+ @Test
+ fun initPip_registersCommandQueueCallback() {
+ underTest.initPip(mPip)
+ verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks::class.java))
+ }
+
+ @Test
+ fun initOneHanded_registersCallbacks() {
+ underTest.initOneHanded(mOneHanded)
+ verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks::class.java))
+ verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer::class.java))
+ verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback::class.java))
+ verify(mOneHanded).registerEventCallback(any(OneHandedEventCallback::class.java))
+ }
+
+ @Test
+ fun initDesktopMode_registersListener() {
+ underTest.initDesktopMode(mDesktopMode)
+ verify(mDesktopMode)
+ .addVisibleTasksListener(
+ any(VisibleTasksListener::class.java),
+ any(Executor::class.java)
+ )
+ }
+
+ @Test
+ fun initRecentTasks_registersListener() {
+ underTest.initRecentTasks(mRecentTasks)
+ verify(mRecentTasks).addAnimationStateListener(any(Executor::class.java), any())
+ }
+
+ @Test
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ fun initRecentTasks_setRecentsBackgroundColorWhenCommunal() =
+ testScope.runTest {
+ val black = Color.valueOf(Color.BLACK)
+ kosmos.fakeCommunalColors.setBackgroundColor(black)
+
+ kosmos.fakeKeyguardRepository.setKeyguardShowing(false)
+
+ underTest.initRecentTasks(mRecentTasks)
+ runCurrent()
+ verify(mRecentTasks).setTransitionBackgroundColor(null)
+ verify(mRecentTasks, never()).setTransitionBackgroundColor(black)
+
+ setDocked(true)
+ // Make communal available
+ kosmos.fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ kosmos.fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
+ kosmos.fakeKeyguardRepository.setKeyguardShowing(true)
+
+ runCurrent()
+
+ verify(mRecentTasks).setTransitionBackgroundColor(black)
+ }
+
+ private fun TestScope.setDocked(docked: Boolean) {
+ kosmos.fakeDockManager.setIsDocked(docked)
+ val event =
+ if (docked) {
+ DockManager.STATE_DOCKED
+ } else {
+ DockManager.STATE_NONE
+ }
+ kosmos.fakeDockManager.setDockEvent(event)
+ runCurrent()
+ }
+
+ private companion object {
+ val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 8e2bd9b..79bf5f1 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -267,6 +267,9 @@
/** True if the clock will react to tone changes in the seed color. */
val isReactiveToTone: Boolean = true,
+
+ /** True if the clock is large frame clock, which will use weather in compose. */
+ val useCustomClockScene: Boolean = false,
)
/** Render configuration options for a clock face. Modifies the way SystemUI behaves. */
@@ -283,6 +286,9 @@
* animation will be used (e.g. a simple translation).
*/
val hasCustomPositionUpdatedAnimation: Boolean = false,
+
+ /** True if the clock is large frame clock, which will use weatherBlueprint in compose. */
+ val useCustomClockScene: Boolean = false,
)
/** Structure for keeping clock-specific settings */
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index 529d609..b62e684b 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -108,7 +108,7 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"መሣሪያ እንደገና ከጀመረ በኋላ ስርዓተ ጥለት ያስፈልጋል"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"መሣሪያ እንደገና ከጀመረ በኋላ ፒን ያስፈልጋል"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"መሣሪያ እንደገና ከጀመረ በኋላ የይለፍ ቃል ያስፈልጋል"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ለተጨማሪ ደህንነት በምትኩ ስርዓተ ጥለት ይጠቀሙ"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ለተጨማሪ ደህንነት በምትኩ ሥርዓተ ጥለት ይጠቀሙ"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ለተጨማሪ ደህንነት በምትኩ ፒን ይጠቀሙ"</string>
<string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ለተጨማሪ ደህንነት በምትኩ የይለፍ ቃል ይጠቀሙ"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"ፒን ለተጨማሪ ደህንነት ያስፈልጋል"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index d07c5b5..427373d 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -21,13 +21,13 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="keyguard_enter_your_pin" msgid="5429932527814874032">"أدخل رقم التعريف الشخصي (PIN)"</string>
- <string name="keyguard_enter_pin" msgid="8114529922480276834">"أدخِل رقم التعريف الشخصي."</string>
+ <string name="keyguard_enter_pin" msgid="8114529922480276834">"أدخِل رقم التعريف الشخصي"</string>
<string name="keyguard_enter_your_pattern" msgid="351503370332324745">"أدخل النقش"</string>
<string name="keyguard_enter_pattern" msgid="7616595160901084119">"ارسم النقش."</string>
<string name="keyguard_enter_your_password" msgid="7225626204122735501">"أدخل كلمة المرور"</string>
<string name="keyguard_enter_password" msgid="6483623792371009758">"أدخِل كلمة المرور."</string>
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"بطاقة غير صالحة."</string>
- <string name="keyguard_charged" msgid="5478247181205188995">"تم الشحن"</string>
+ <string name="keyguard_charged" msgid="5478247181205188995">"اكتمل الشحن"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن لاسلكيًا"</string>
<string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن"</string>
@@ -80,9 +80,9 @@
<string name="kg_face_locked_out" msgid="2751559491287575">"يتعذّر فتح القفل بالوجه. أجريت محاولات كثيرة جدًا."</string>
<string name="kg_fp_locked_out" msgid="6228277682396768830">"يتعذّر الفتح ببصمة الإصبع. أجريت محاولات كثيرة جدًا."</string>
<string name="kg_trust_agent_disabled" msgid="5400691179958727891">"ميزة \"الوكيل المعتمد\" غير متاحة."</string>
- <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"أجريت محاولات كثيرة جدًا بإدخال رقم تعريف شخصي خاطئ."</string>
- <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"أجريت محاولات كثيرة جدًا برسم نقش خاطئ."</string>
- <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"أجريت محاولات كثيرة جدًا بإدخال كلمة مرور خاطئة."</string>
+ <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"أجريت محاولات كثيرة جدًا باستخدام رقم تعريف شخصي خاطئ"</string>
+ <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"أجريت محاولات كثيرة جدًا باستخدام نقش خاطئ"</string>
+ <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"أجريت محاولات كثيرة جدًا باستخدام كلمة مرور خاطئة"</string>
<string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{أعِد المحاولة خلال ثانية واحدة.}zero{أعِد المحاولة خلال # ثانية.}two{أعِد المحاولة خلال ثانيتين.}few{أعِد المحاولة خلال # ثوانٍ.}many{أعِد المحاولة خلال # ثانية.}other{أعِد المحاولة خلال # ثانية.}}"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"أدخل رقم التعريف الشخصي لشريحة SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"أدخل رقم التعريف الشخصي لشريحة SIM التابعة للمشغّل \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
@@ -108,9 +108,9 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"يجب رسم النقش بعد إعادة تشغيل الجهاز."</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"يجب إدخال رقم التعريف الشخصي بعد إعادة تشغيل الجهاز."</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"يجب إدخال كلمة المرور بعد إعادة تشغيل الجهاز."</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"لمزيد من الأمان، استخدِم النقش بدلاً من ذلك."</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"لمزيد من الأمان، أدخِل رقم التعريف الشخصي بدلاً من ذلك."</string>
- <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"لمزيد من الأمان، أدخِل كلمة المرور بدلاً من ذلك."</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"لمزيد من الأمان، يجب استخدام النقش"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"لمزيد من الأمان، يجب إدخال رقم التعريف الشخصي"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"لمزيد من الأمان، يجب إدخال كلمة المرور"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"يجب إدخال رقم التعريف الشخصي لمزيد من الأمان"</string>
<string name="kg_prompt_added_security_pattern" msgid="1017068086102168544">"يجب رسم النقش لمزيد من الأمان"</string>
<string name="kg_prompt_added_security_password" msgid="6053156069765029006">"يجب إدخال كلمة المرور لمزيد من الأمان"</string>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index ce8ebd3..4ed7e27 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -108,7 +108,7 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"ডিভাইচ ৰিষ্টাৰ্ট হোৱাৰ পাছত আৰ্হিৰ আৱশ্যক"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"ডিভাইচ ৰিষ্টাৰ্ট হোৱাৰ পাছত পিনৰ আৱশ্যক"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"ডিভাইচ ৰিষ্টাৰ্ট হোৱাৰ পাছত পাছৱৰ্ডৰ আৱশ্যক"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"অতিৰিক্ত সুৰক্ষাৰ বাবে, ইয়াৰ পৰিৱৰ্তে আৰ্হি ব্যৱহাৰ কৰক"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"অতিৰিক্ত সুৰক্ষাৰ বাবে, আৰ্হি ব্যৱহাৰ কৰক"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"অতিৰিক্ত সুৰক্ষাৰ বাবে, ইয়াৰ পৰিৱৰ্তে পিন ব্যৱহাৰ কৰক"</string>
<string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"অতিৰিক্ত সুৰক্ষাৰ বাবে, ইয়াৰ পৰিৱৰ্তে পাছৱৰ্ড ব্যৱহাৰ কৰক"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"অতিৰিক্ত সুৰক্ষাৰ বাবে পিন দিয়াটো বাধ্যতামূলক"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index 9677945..4a5e789 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -108,9 +108,9 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"Uzorak je potreban nakon ponovnog pokretanja uređaja"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"PIN je potreban nakon ponovnog pokretanja uređaja"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"Lozinka je potrebna nakon pokretanja uređaja"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Radi dodatne zaštite, umjesto toga koristite uzorak"</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Radi dodatne zaštite, umjesto toga koristite PIN"</string>
- <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Radi dodatne zašitite, umjesto toga koristite lozinku"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Za dodatnu sigurnost koristite uzorak"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Za dodatnu sigurnost koristite PIN"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Za dodatnu sigurnost koristite lozinku"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"PIN je potreban radi dodatne sigurnosti"</string>
<string name="kg_prompt_added_security_pattern" msgid="1017068086102168544">"Uzorak je potreban radi dodatne sigurnosti"</string>
<string name="kg_prompt_added_security_password" msgid="6053156069765029006">"Lozinka je potrebna radi dodatne sigurnosti"</string>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index 8e97e84..5a03cec 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -83,7 +83,7 @@
<string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Příliš mnoho pokusů s nesprávným kódem PIN"</string>
<string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Příliš mnoho pokusů s nesprávným gestem"</string>
<string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Příliš mnoho pokusů s nesprávným heslem"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Zkuste to znovu za # sekundu.}few{Zkuste to znovu za # sekundy.}many{Zkuste to znovu za # sekundy.}other{Zkuste to znovu za # sekund.}}"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Zkuste to znovu za # sekundu}few{Zkuste to znovu za # sekundy}many{Zkuste to znovu za # sekundy}other{Zkuste to znovu za # sekund}}"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Zadejte kód PIN SIM karty."</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Zadejte kód PIN SIM karty <xliff:g id="CARRIER">%1$s</xliff:g>."</string>
<string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> eSIM kartu deaktivujte, chcete-li zařízení používat bez mobilních služeb."</string>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index 2245710..195fdef 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"Gib deine PIN ein"</string>
+ <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"PIN eingeben"</string>
<string name="keyguard_enter_pin" msgid="8114529922480276834">"Gib die PIN ein"</string>
<string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Muster eingeben"</string>
<string name="keyguard_enter_pattern" msgid="7616595160901084119">"Zeichne das Muster"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index 45e2a9d..bfb2ed0 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -83,7 +83,7 @@
<string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Demasiados intentos con PIN incorrecto"</string>
<string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Demasiados intentos con patrón incorrecto"</string>
<string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Demasiados intentos con contraseña incorrecta"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Vuelve a intentarlo en # segundo.}many{Vuelve a intentarlo en # segundos.}other{Vuelve a intentarlo en # segundos.}}"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Vuelve a intentarlo en # segundo}many{Vuelve a intentarlo en # segundos}other{Vuelve a intentarlo en # segundos}}"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Ingresa el PIN de la tarjeta SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Ingresa el PIN de la tarjeta SIM de \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
<string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Inhabilita la tarjeta eSIM para usar el dispositivo sin servicio móvil."</string>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index ea1dd13..c59bdc1 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -108,9 +108,9 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"Kuvio tarvitaan uudelleenkäynnistyksen jälkeen"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"PIN-koodi tarvitaan uudelleenkäynnistyksen jälkeen"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"Salasana tarvitaan uudelleenkäynnistyksen jälkeen"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Lisäsuojaa saat, kun käytät sen sijaan kuviota"</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Lisäsuojaa saat, kun käytät sen sijaan PIN-koodia"</string>
- <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Lisäsuojaa saat, kun käytät sen sijaan salasanaa"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Käytä kuviota, niin saat lisäsuojaa"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Käytä PIN-koodia, niin saat lisäsuojaa"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Käytä salasanaa, niin saat lisäsuojaa"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"PIN-koodi vaaditaan suojauksen parantamiseksi."</string>
<string name="kg_prompt_added_security_pattern" msgid="1017068086102168544">"Kuvio vaaditaan suojauksen parantamiseksi."</string>
<string name="kg_prompt_added_security_password" msgid="6053156069765029006">"Salasana vaaditaan suojauksen parantamiseksi."</string>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index 6b51ac2..e5cd788 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -108,9 +108,9 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"Requírese o padrón tras reiniciar o dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"Requírese o PIN tras reiniciar o dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"Requírese o contrasinal tras reiniciar o dispositivo"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Utiliza un padrón para obter maior seguranza"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Utiliza un padrón para unha maior seguranza"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Utiliza un PIN para obter maior seguranza"</string>
- <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Utiliza un contrasinal para obter maior seguranza"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Utiliza un contrasinal para unha maior seguranza"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"É necesario poñer o PIN como medida de seguranza adicional"</string>
<string name="kg_prompt_added_security_pattern" msgid="1017068086102168544">"É necesario poñer o padrón como medida de seguranza adicional"</string>
<string name="kg_prompt_added_security_password" msgid="6053156069765029006">"É necesario poñer o contrasinal como medida de seguranza adicional"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index a762b49..2b01903 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -109,8 +109,8 @@
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"डिवाइस रीस्टार्ट करने पर, पिन डालना ज़रूरी है"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"डिवाइस रीस्टार्ट करने पर, पासवर्ड डालना ज़रूरी है"</string>
<string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ज़्यादा सुरक्षा के लिए, इसके बजाय पैटर्न का इस्तेमाल करें"</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ज़्यादा सुरक्षा के लिए, इसके बजाय पिन का इस्तेमाल करें"</string>
- <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ज़्यादा सुरक्षा के लिए, इसके बजाय पासवर्ड का इस्तेमाल करें"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ज़्यादा सुरक्षा के लिए, पिन का इस्तेमाल करें"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ज़्यादा सुरक्षा के लिए, पासवर्ड का इस्तेमाल करें"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"अतिरिक्त सुरक्षा के लिए पिन ज़रूरी है"</string>
<string name="kg_prompt_added_security_pattern" msgid="1017068086102168544">"अतिरिक्त सुरक्षा के लिए पैटर्न ज़रूरी है"</string>
<string name="kg_prompt_added_security_password" msgid="6053156069765029006">"अतिरिक्त सुरक्षा के लिए पासवर्ड ज़रूरी है"</string>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index dc5b0b8..e2ac8b6 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -80,10 +80,10 @@
<string name="kg_face_locked_out" msgid="2751559491287575">"Tidak dapat membuka kunci dengan wajah. Terlalu banyak upaya gagal."</string>
<string name="kg_fp_locked_out" msgid="6228277682396768830">"Tidak dapat membuka kunci dengan sidik jari. Terlalu banyak upaya gagal."</string>
<string name="kg_trust_agent_disabled" msgid="5400691179958727891">"Perangkat tepercaya tidak tersedia"</string>
- <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Terlalu banyak upaya dengan PIN yang salah"</string>
- <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Terlalu banyak upaya dengan pola yang salah"</string>
- <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Terlalu banyak upaya dengan sandi yang salah"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Coba lagi dalam # detik.}other{Coba lagi dalam # detik.}}"</string>
+ <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Terlalu banyak pemakaian PIN yang salah"</string>
+ <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Terlalu banyak pemakaian pola yang salah"</string>
+ <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Terlalu banyak pemakaian sandi yang salah"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Coba lagi setelah # detik.}other{Coba lagi setelah # detik.}}"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Masukkan PIN SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Masukkan PIN SIM \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
<string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Nonaktifkan eSIM untuk menggunakan perangkat tanpa layanan seluler."</string>
@@ -108,9 +108,9 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"Pola diperlukan setelah perangkat dimulai ulang"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"PIN diperlukan setelah perangkat dimulai ulang"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"Sandi diperlukan setelah perangkat dimulai ulang"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Untuk keamanan tambahan, gunakan pola"</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Untuk keamanan tambahan, gunakan PIN"</string>
- <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Untuk keamanan tambahan, gunakan sandi"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Agar lebih aman, gunakan pola"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Agar lebih aman, gunakan PIN"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Agar lebih aman, gunakan sandi"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"PIN diperlukan untuk keamanan tambahan"</string>
<string name="kg_prompt_added_security_pattern" msgid="1017068086102168544">"Pola diperlukan untuk keamanan tambahan"</string>
<string name="kg_prompt_added_security_password" msgid="6053156069765029006">"Sandi diperlukan untuk keamanan tambahan"</string>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index 20808db..2deefd0 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -108,9 +108,9 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"Mynsturs er krafist eftir að tækið er endurræst"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"PIN-númers er krafist eftir að tækið er endurræst"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"Aðgangsorðs er krafist eftir að tækið er endurræst"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Fyrir aukið öryggi skaltu nota mynstur í staðinn"</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Fyrir aukið öryggi skaltu nota PIN-númer í staðinn"</string>
- <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Fyrir aukið öryggi skaltu nota aðgangsorð í staðinn"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Til að auka öryggi skaltu nota mynstur í staðinn"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Til að auka öryggi skaltu nota PIN-númer í staðinn"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Til að auka öryggi skaltu nota aðgangsorð í staðinn"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"PIN-númers er krafist af öryggisástæðum"</string>
<string name="kg_prompt_added_security_pattern" msgid="1017068086102168544">"Mynsturs er krafist af öryggisástæðum"</string>
<string name="kg_prompt_added_security_password" msgid="6053156069765029006">"Aðgangsorðs er krafist af öryggisástæðum"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index 71969c0..9752eca 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -80,9 +80,9 @@
<string name="kg_face_locked_out" msgid="2751559491287575">"顔認証でロックを解除できません。何度もログインに失敗したためログインできません。"</string>
<string name="kg_fp_locked_out" msgid="6228277682396768830">"指紋でロックを解除できません。何度もログインに失敗したためログインできません。"</string>
<string name="kg_trust_agent_disabled" msgid="5400691179958727891">"信頼エージェントは利用できません"</string>
- <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"間違った PIN による試行回数が上限を超えました"</string>
- <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"間違ったパターンによる試行回数が上限を超えました"</string>
- <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"間違ったパスワードによる試行回数が上限を超えました"</string>
+ <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"間違った PIN による試行回数が上限に達しました"</string>
+ <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"間違ったパターンによる試行回数が上限に達しました"</string>
+ <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"間違ったパスワードによる試行回数が上限に達しました"</string>
<string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{# 秒後にもう一度お試しください。}other{# 秒後にもう一度お試しください。}}"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"SIM PIN を入力してください。"</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"「<xliff:g id="CARRIER">%1$s</xliff:g>」の SIM PIN を入力してください。"</string>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index b816748..733e29c 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -80,9 +80,9 @@
<string name="kg_face_locked_out" msgid="2751559491287575">"មិនអាចដោះសោដោយប្រើមុខទេ។ ព្យាយាមច្រើនដងពេក។"</string>
<string name="kg_fp_locked_out" msgid="6228277682396768830">"មិនអាចដោះសោដោយប្រើស្នាមម្រាមដៃទេ។ ព្យាយាមច្រើនដងពេក។"</string>
<string name="kg_trust_agent_disabled" msgid="5400691179958727891">"ភ្នាក់ងារទុកចិត្តមិនទំនេរទេ"</string>
- <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"ព្យាយាមច្រើនដងពេកដោយប្រើកូដ PIN មិនត្រឹមត្រូវ"</string>
- <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"ព្យាយាមច្រើនដងពេកដោយប្រើលំនាំមិនត្រឹមត្រូវ"</string>
- <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"ព្យាយាមច្រើនដងពេកដោយប្រើពាក្យសម្ងាត់មិនត្រឹមត្រូវ"</string>
+ <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"ព្យាយាមដោយប្រើកូដ PIN មិនត្រឹមត្រូវច្រើនដងពេក"</string>
+ <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"ព្យាយាមដោយប្រើលំនាំមិនត្រឹមត្រូវច្រើនដងពេក"</string>
+ <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"ព្យាយាមដោយប្រើពាក្យសម្ងាត់មិនត្រឹមត្រូវច្រើនដងពេក"</string>
<string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{ព្យាយាមម្តងទៀតក្នុងរយៈពេល # វិនាទីទៀត។}other{ព្យាយាមម្តងទៀតក្នុងរយៈពេល # វិនាទីទៀត។}}"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"បញ្ចូលកូដ PIN របស់ស៊ីម។"</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"បញ្ចូលកូដ PIN របស់ស៊ីមសម្រាប់ \"<xliff:g id="CARRIER">%1$s</xliff:g>\"។"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index 8d86a8d..69154ae 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -108,7 +108,7 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"기기가 다시 시작되어 패턴을 입력해야 합니다."</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"기기가 다시 시작되어 PIN을 입력해야 합니다."</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"기기가 다시 시작되어 비밀번호를 입력해야 합니다."</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"보안 강화를 위해 대신 패턴 사용"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"보안 강화를 위해 패턴 사용"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"보안 강화를 위해 대신 PIN 사용"</string>
<string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"보안 강화를 위해 대신 비밀번호 사용"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"보안 강화를 위해 PIN이 필요합니다."</string>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index 6bfbd64..de53f9f 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -49,7 +49,7 @@
<string name="disable_carrier_button_text" msgid="7153361131709275746">"eSIM-картаны өчүрүү"</string>
<string name="error_disable_esim_title" msgid="3802652622784813119">"eSIM-картаны өчүрүүгө болбойт"</string>
<string name="error_disable_esim_msg" msgid="2441188596467999327">"Катадан улам eSIM-картаны өчүрүүгө болбойт."</string>
- <string name="keyboardview_keycode_enter" msgid="6727192265631761174">"Киргизүү"</string>
+ <string name="keyboardview_keycode_enter" msgid="6727192265631761174">"Enter"</string>
<string name="kg_wrong_pattern" msgid="5907301342430102842">"Графикалык ачкыч туура эмес"</string>
<string name="kg_wrong_pattern_try_again" msgid="3603524940234151881">"Графклк ачкч тура эмс. Кайтлап крүңз."</string>
<string name="kg_wrong_password" msgid="4143127991071670512">"Сырсөз туура эмес"</string>
@@ -80,9 +80,9 @@
<string name="kg_face_locked_out" msgid="2751559491287575">"Жүз менен кулпусу ачылбай жатат. Өтө көп жолу аракет кылдыңыз."</string>
<string name="kg_fp_locked_out" msgid="6228277682396768830">"Манжа изи менен кулпусу ачылбай жатат. Өтө көп жолу аракет кылдыңыз."</string>
<string name="kg_trust_agent_disabled" msgid="5400691179958727891">"Ишеним агенти жеткиликсиз"</string>
- <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Туура эмес PIN код менен өтө көп аракет"</string>
- <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Туура эмес графикалык ачкыч менен өтө көп аракет"</string>
- <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Туура эмес сырсөз менен өтө көп аракет"</string>
+ <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"PIN код өтө көп жолу туура эмес киргизилди."</string>
+ <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Графикалык ачкыч өтө көп жолу туура эмес тартылды."</string>
+ <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Сырсөздү өтө көп жолу туура эмес киргиздиңиз."</string>
<string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{# секунддан кийин кайталаңыз.}other{# секунддан кийин кайталаңыз.}}"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"SIM-картанын PIN кодун киргизиңиз."</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" SIM-картасынын PIN кодун киргизиңиз."</string>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index 3e110b5..6c57d89 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -49,7 +49,7 @@
<string name="disable_carrier_button_text" msgid="7153361131709275746">"Оневозможи ја eSIM"</string>
<string name="error_disable_esim_title" msgid="3802652622784813119">"Не може да се оневозможи eSIM"</string>
<string name="error_disable_esim_msg" msgid="2441188596467999327">"eSIM-картичката не може да се оневозможи поради грешка."</string>
- <string name="keyboardview_keycode_enter" msgid="6727192265631761174">"Внеси"</string>
+ <string name="keyboardview_keycode_enter" msgid="6727192265631761174">"Enter"</string>
<string name="kg_wrong_pattern" msgid="5907301342430102842">"Погрешна шема"</string>
<string name="kg_wrong_pattern_try_again" msgid="3603524940234151881">"Погрешна шема. Обидете се повторно."</string>
<string name="kg_wrong_password" msgid="4143127991071670512">"Погрешна лозинка"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index ecf843d..0112d5d 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -83,7 +83,7 @@
<string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Terlalu banyak percubaan dengan PIN yang salah"</string>
<string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Terlalu banyak percubaan dengan corak yang salah"</string>
<string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Terlalu banyak percubaan dengan kata laluan yang salah"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Cuba lagi dalam # saat.}other{Cuba lagi dalam # saat.}}"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Cuba lagi selepas # saat.}other{Cuba lagi selepas # saat.}}"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Masukkan PIN SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Masukkan PIN SIM untuk \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
<string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Lumpuhkan eSIM untuk menggunakan peranti tanpa perkhidmatan mudah alih."</string>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index 95d638e..f74eb66 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -108,7 +108,7 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"စက်ကို ပြန်စပြီးနောက် ပုံဖော်ခြင်းလိုအပ်သည်"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"စက်ကို ပြန်စပြီးနောက် ပင်နံပါတ်လိုအပ်သည်"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"စက်ကို ပြန်စပြီးနောက် စကားဝှက်လိုအပ်သည်"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ထပ်ဆောင်းလုံခြုံရေးအတွက် ၎င်းအစား ပုံစံသုံးပါ"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ထပ်ဆောင်းလုံခြုံရေးအတွက် ပုံစံကို အစားထိုးသုံးပါ"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ထပ်ဆောင်းလုံခြုံရေးအတွက် ၎င်းအစား ပင်နံပါတ်သုံးပါ"</string>
<string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ထပ်ဆောင်းလုံခြုံရေးအတွက် ၎င်းအစား စကားဝှက်သုံးပါ"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"ထပ်ဆောင်း လုံခြုံရေးအတွက် ပင်နံပါတ် လိုအပ်သည်"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index 219072b..27856d6 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -24,7 +24,7 @@
<string name="keyguard_enter_pin" msgid="8114529922480276834">"PIN हाल्नुहोस्"</string>
<string name="keyguard_enter_your_pattern" msgid="351503370332324745">"प्याटर्न हाल्नुहोस्"</string>
<string name="keyguard_enter_pattern" msgid="7616595160901084119">"प्याटर्न कोर्नुहोस्"</string>
- <string name="keyguard_enter_your_password" msgid="7225626204122735501">"पासवर्ड हाल्नुहोस्…"</string>
+ <string name="keyguard_enter_your_password" msgid="7225626204122735501">"पासवर्ड हाल्नुहोस्"</string>
<string name="keyguard_enter_password" msgid="6483623792371009758">"पासवर्ड हाल्नुहोस्"</string>
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"अमान्य कार्ड।"</string>
<string name="keyguard_charged" msgid="5478247181205188995">"चार्ज भयो"</string>
@@ -108,9 +108,9 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"डिभाइस रिस्टार्ट भएपछि प्याटर्न कोर्नु पर्ने हुन्छ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"डिभाइस रिस्टार्ट भएपछि PIN हाल्नु पर्ने हुन्छ"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"डिभाइस रिस्टार्ट भएपछि पासवर्ड हाल्नु पर्ने हुन्छ"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"अतिरिक्त सुरक्षाका लागि यो प्रमाणीकरण विधिको साटो प्याटर्न प्रयोग गर्नुहोस्"</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"अतिरिक्त सुरक्षाका लागि यो प्रमाणीकरण विधिको साटो पिन प्रयोग गर्नुहोस्"</string>
- <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"अतिरिक्त सुरक्षाका लागि यो प्रमाणीकरण विधिको साटो पासवर्ड प्रयोग गर्नुहोस्"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"अतिरिक्त सुरक्षाका लागि यसको साटो पासवर्ड प्रयोग गर्नुहोस्"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"अतिरिक्त सुरक्षाका लागि यसको साटो पासवर्ड प्रयोग गर्नुहोस्"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"अतिरिक्त सुरक्षाका लागि यसको साटो पासवर्ड प्रयोग गर्नुहोस्"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"अतिरिक्त सुरक्षाका लागि PIN चाहिन्छ"</string>
<string name="kg_prompt_added_security_pattern" msgid="1017068086102168544">"अतिरिक्त सुरक्षाका लागि प्याटर्न चाहिन्छ"</string>
<string name="kg_prompt_added_security_password" msgid="6053156069765029006">"अतिरिक्त सुरक्षाका लागि पासवर्ड चाहिन्छ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index 0206403..9fb9e1b 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -22,7 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="keyguard_enter_your_pin" msgid="5429932527814874032">"Geef je pincode op"</string>
<string name="keyguard_enter_pin" msgid="8114529922480276834">"Voer pincode in"</string>
- <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Geef je patroon op"</string>
+ <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Voer je patroon in"</string>
<string name="keyguard_enter_pattern" msgid="7616595160901084119">"Teken het patroon"</string>
<string name="keyguard_enter_your_password" msgid="7225626204122735501">"Voer je wachtwoord in"</string>
<string name="keyguard_enter_password" msgid="6483623792371009758">"Geef het wachtwoord op"</string>
@@ -80,9 +80,9 @@
<string name="kg_face_locked_out" msgid="2751559491287575">"Kan niet ontgrendelen met gezicht. Te veel pogingen."</string>
<string name="kg_fp_locked_out" msgid="6228277682396768830">"Niet ontgrendeld met vingerafdruk. Te veel pogingen."</string>
<string name="kg_trust_agent_disabled" msgid="5400691179958727891">"Trust agent is niet beschikbaar"</string>
- <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Te veel pogingen met onjuiste pincode"</string>
- <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Te veel pogingen met onjuist patroon"</string>
- <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Te veel pogingen met onjuist wachtwoord"</string>
+ <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Pincode te vaak verkeerd ingevoerd"</string>
+ <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Patroon te vaak verkeerd getekend"</string>
+ <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Wachtwoord te vaak verkeerd ingevoerd"</string>
<string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Probeer het over # seconde opnieuw.}other{Probeer het over # seconden opnieuw.}}"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Geef de pincode van de simkaart op."</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Geef de pincode voor de simkaart van \'<xliff:g id="CARRIER">%1$s</xliff:g>\' op."</string>
@@ -108,9 +108,9 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"Patroon is vereist na opnieuw opstarten apparaat"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"Pincode is vereist na opnieuw opstarten apparaat"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"Wachtwoord is vereist na opnieuw opstarten apparaat"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Gebruik in plaats daarvan het patroon voor extra beveiliging"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Gebruik het patroon voor extra beveiliging"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Gebruik de pincode voor extra beveiliging"</string>
- <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Gebruik in plaats daarvan het wachtwoord voor extra beveiliging"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Gebruik het wachtwoord voor extra beveiliging"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"Pincode vereist voor extra beveiliging"</string>
<string name="kg_prompt_added_security_pattern" msgid="1017068086102168544">"Patroon vereist voor extra beveiliging"</string>
<string name="kg_prompt_added_security_password" msgid="6053156069765029006">"Wachtwoord vereist voor extra beveiliging"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 4a89070..f25e9c5 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -83,7 +83,7 @@
<string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Zbyt wiele nieudanych prób wpisania kodu PIN"</string>
<string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Zbyt wiele nieudanych prób narysowania wzoru"</string>
<string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Zbyt wiele nieudanych prób wpisania hasła"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Spróbuj ponownie za # sekundę.}few{Spróbuj ponownie za # sekundy.}many{Spróbuj ponownie za # sekund.}other{Spróbuj ponownie za # sekundy.}}"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Spróbuj ponownie za # sekundę}few{Spróbuj ponownie za # sekundy}many{Spróbuj ponownie za # sekund}other{Spróbuj ponownie za # sekundy}}"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Wpisz kod PIN karty SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Wpisz kod PIN karty SIM „<xliff:g id="CARRIER">%1$s</xliff:g>”."</string>
<string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Wyłącz kartę eSIM, by używać urządzenia bez usługi sieci komórkowej."</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index 1ae1aeb..b292204 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -24,7 +24,7 @@
<string name="keyguard_enter_pin" msgid="8114529922480276834">"Introduza o PIN"</string>
<string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Introduza o padrão"</string>
<string name="keyguard_enter_pattern" msgid="7616595160901084119">"Desenhe o padrão"</string>
- <string name="keyguard_enter_your_password" msgid="7225626204122735501">"Introduza a palavra-passe."</string>
+ <string name="keyguard_enter_your_password" msgid="7225626204122735501">"Introduza a palavra-passe"</string>
<string name="keyguard_enter_password" msgid="6483623792371009758">"Introduza a palavra-passe"</string>
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Cartão inválido."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Carregada"</string>
@@ -108,9 +108,9 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"Padrão necessário após reiniciar o dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"PIN necessário após reiniciar o dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"Palavra-passe necessária após reiniciar dispositivo"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Para uma segurança adicional, use antes o padrão"</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Para uma segurança adicional, use antes o PIN"</string>
- <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Para uma segurança adicional, use antes a palavra-passe"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Para segurança adicional, use o padrão"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Para segurança adicional, use o PIN"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Para segurança adicional, use a palavra-passe"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"Para segurança adicional, é necessária um PIN"</string>
<string name="kg_prompt_added_security_pattern" msgid="1017068086102168544">"Para segurança adicional, é necessário um padrão"</string>
<string name="kg_prompt_added_security_password" msgid="6053156069765029006">"Para segurança adicional, é necessária uma palavra-passe"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index aa2d080..fd90d08 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -81,7 +81,7 @@
<string name="kg_fp_locked_out" msgid="6228277682396768830">"Превышен лимит попыток разблокировки отпечатком."</string>
<string name="kg_trust_agent_disabled" msgid="5400691179958727891">"Агент доверия недоступен."</string>
<string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Слишком много неудачных попыток ввести PIN-код."</string>
- <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Слишком много неудачных попыток ввести граф. ключ."</string>
+ <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Слишком много неудачных попыток ввести графический ключ."</string>
<string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Слишком много неудачных попыток ввести пароль."</string>
<string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Повторите попытку через # секунду.}one{Повторите попытку через # секунду.}few{Повторите попытку через # секунды.}many{Повторите попытку через # секунд.}other{Повторите попытку через # секунды.}}"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Введите PIN-код SIM-карты."</string>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index 5725fe0..9b0647a 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -108,7 +108,7 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"Po reštarte zariadenia sa vyžaduje vzor"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"Po reštarte zariadenia sa vyžaduje kód PIN"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"Po reštarte zariadenia sa vyžaduje heslo"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"V rámci zvýšenia zabezpečenia použite radšej vzor"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Z bezpečnostných dôvodov použite radšej vzor"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"V rámci zvýšenia zabezpečenia použite radšej PIN"</string>
<string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"V rámci zvýšenia zabezpečenia použite radšej heslo"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"Zvýšenie zabezpečenia vyžaduje kód PIN"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index bfe5ae0..cb17459 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -108,7 +108,7 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"Kailangan ang pattern pagka-restart ng device"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"Kailangan ang PIN pagka-restart ng device"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"Kailangan ang password pagka-restart ng device"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Para sa karagdagang seguridad, gumamit na lang ng pattern"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Para sa karagdagang seguridad, gumamit ng pattern"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Para sa karagdagang seguridad, gumamit na lang ng PIN"</string>
<string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Para sa karagdagang seguridad, gumamit na lang ng password"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"Kinakailangan ang PIN para sa karagdagang seguridad"</string>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index bbd319c3..72ea850 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -108,7 +108,7 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"Cần vẽ hình mở khoá sau khi khởi động lại thiết bị"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"Cần nhập mã PIN sau khi khởi động lại thiết bị"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"Cần nhập mật khẩu sau khi khởi động lại thiết bị"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Để tăng cường bảo mật, hãy sử dụng hình mở khoá"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Để tăng cường bảo mật, hãy dùng hình mở khoá"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Để tăng cường bảo mật, hãy sử dụng mã PIN"</string>
<string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Để tăng cường bảo mật, hãy sử dụng mật khẩu"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"Yêu cầu mã PIN để tăng cường bảo mật"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index a316e8c..7d74c9c 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -22,7 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="keyguard_enter_your_pin" msgid="5429932527814874032">"输入您的 PIN 码"</string>
<string name="keyguard_enter_pin" msgid="8114529922480276834">"输入 PIN 码"</string>
- <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"绘制您的图案"</string>
+ <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"绘制解锁图案"</string>
<string name="keyguard_enter_pattern" msgid="7616595160901084119">"绘制图案"</string>
<string name="keyguard_enter_your_password" msgid="7225626204122735501">"输入您的密码"</string>
<string name="keyguard_enter_password" msgid="6483623792371009758">"输入密码"</string>
@@ -108,7 +108,7 @@
<string name="kg_prompt_reason_restart_pattern" msgid="3321211830602827742">"设备重启后,必须绘制图案才能解锁"</string>
<string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"设备重启后,必须输入 PIN 码才能解锁"</string>
<string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"设备重启后,必须输入密码才能解锁"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"为增强安全性,请改用图案"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"为增强安全性,请改用解锁图案"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"为增强安全性,请改用 PIN 码"</string>
<string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"为增强安全性,请改用密码"</string>
<string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"需要输入 PIN 码以进一步确保安全"</string>
diff --git a/packages/SystemUI/res-product/values-ar/strings.xml b/packages/SystemUI/res-product/values-ar/strings.xml
index 4d4d8d0..0ddb911 100644
--- a/packages/SystemUI/res-product/values-ar/strings.xml
+++ b/packages/SystemUI/res-product/values-ar/strings.xml
@@ -34,10 +34,10 @@
<string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"أخطأت في محاولة فتح قفل الهاتف <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستتم إزالة الملف الشخصي لهذا المستخدم، ومن ثم يتم حذف جميع بياناته."</string>
<string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"أخطأت في محاولة فتح قفل الجهاز اللوحي <xliff:g id="NUMBER">%d</xliff:g> مرة. ستتم إزالة الملف الشخصي لهذا المستخدم، ومن ثم يتم حذف جميع بياناته."</string>
<string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"أخطأت في محاولة فتح قفل الهاتف <xliff:g id="NUMBER">%d</xliff:g> مرة. ستتم إزالة الملف الشخصي لهذا المستخدم، ومن ثم يتم حذف جميع بياناته."</string>
- <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"أخطأت في محاولة فتح قفل الجهاز اللوحي <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستتم إزالة الملف الشخصي للعمل، ومن ثم يتم حذف جميع بيانات الملف الشخصي."</string>
- <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"أخطأت في محاولة فتح قفل الهاتف <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستتم إزالة الملف الشخصي للعمل، ومن ثم يتم حذف جميع بيانات الملف الشخصي."</string>
- <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"أخطأت في محاولة فتح قفل الجهاز اللوحي <xliff:g id="NUMBER">%d</xliff:g> مرة. ستتم إزالة الملف الشخصي للعمل، ومن ثم يتم حذف جميع بياناته."</string>
- <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"أخطأت في محاولة فتح قفل الهاتف <xliff:g id="NUMBER">%d</xliff:g> مرة. ستتم إزالة الملف الشخصي للعمل، ومن ثم يتم حذف جميع بياناته."</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"أخطأت في محاولة فتح قفل الجهاز اللوحي <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستتم إزالة ملف العمل، ومن ثم يتم حذف جميع بيانات الملف الشخصي."</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"أخطأت في محاولة فتح قفل الهاتف <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستتم إزالة ملف العمل، ومن ثم يتم حذف جميع بيانات الملف الشخصي."</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"أخطأت في محاولة فتح قفل الجهاز اللوحي <xliff:g id="NUMBER">%d</xliff:g> مرة. ستتم إزالة ملف العمل، ومن ثم يتم حذف جميع بياناته."</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"أخطأت في محاولة فتح قفل الهاتف <xliff:g id="NUMBER">%d</xliff:g> مرة. ستتم إزالة ملف العمل، ومن ثم يتم حذف جميع بياناته."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"رسمت نقش فتح القفل بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستُطالَب بفتح قفل الجهاز اللوحي باستخدام معلومات حساب بريد إلكتروني.\n\n يُرجى إعادة المحاولة خلال <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانية."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"رسمت نقش فتح القفل بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستُطالَب بفتح قفل الهاتف باستخدام حساب بريد إلكتروني.\n\n يُرجى إعادة المحاولة خلال <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانية."</string>
<string name="thermal_shutdown_title" product="default" msgid="8039593017174903505">"تم إطفاء الهاتف بسبب ارتفاع درجة حرارته"</string>
diff --git a/packages/SystemUI/res/drawable/ic_noise_aware.xml b/packages/SystemUI/res/drawable/ic_noise_aware.xml
deleted file mode 100644
index 5482641..0000000
--- a/packages/SystemUI/res/drawable/ic_noise_aware.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="960"
- android:viewportHeight="960"
- android:tint="?attr/colorControlNormal">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M440,82Q450,81 460,80.5Q470,80 480,80Q491,80 500.5,80.5Q510,81 520,82L520,162Q510,160 500.5,160Q491,160 480,160Q469,160 459.5,160Q450,160 440,162L440,82ZM272,138Q289,127 306.5,119Q324,111 343,104L378,176Q358,182 340.5,190.5Q323,199 306,210L272,138ZM654,210Q637,199 619.5,190.5Q602,182 582,176L617,104Q636,111 653.5,119Q671,127 688,138L654,210ZM753,311Q742,294 729,278.5Q716,263 702,249L765,199Q779,213 792,228.5Q805,244 816,261L753,311ZM143,263Q154,246 166.5,230.5Q179,215 193,201L256,251Q242,265 229.5,280.5Q217,296 206,313L143,263ZM83,428Q85,408 90,388.5Q95,369 101,350L180,368Q173,387 168.5,406.5Q164,426 162,446L83,428ZM799,449Q797,429 792.5,409Q788,389 781,370L859,352Q865,371 870,390.5Q875,410 877,430L799,449ZM781,590Q788,571 792,552Q796,533 798,513L877,531Q875,551 870,570.5Q865,590 859,609L781,590ZM162,514Q164,534 168.5,553.5Q173,573 180,592L101,610Q95,591 90,571.5Q85,552 83,532L162,514ZM705,708Q719,694 731,678.5Q743,663 754,646L818,696Q807,713 794.5,728.5Q782,744 768,758L705,708ZM194,760Q180,746 167.5,730Q155,714 144,697L206,647Q217,664 229.5,680Q242,696 256,710L194,760ZM583,783Q603,776 620,768Q637,760 654,749L689,821Q672,832 654.5,840.5Q637,849 618,856L583,783ZM344,857Q325,850 307,841.5Q289,833 272,822L307,750Q324,761 341.5,769.5Q359,778 379,784L344,857ZM480,880Q470,880 460,879.5Q450,879 440,878L440,798Q453,800 480,800Q491,800 500.5,800Q510,800 520,798L520,878Q510,879 500.5,879.5Q491,880 480,880ZM520,720Q482,720 450.5,697Q419,674 406,638Q403,629 399.5,620.5Q396,612 389,605L334,550Q308,524 294,490.5Q280,457 280,420Q280,345 332.5,292.5Q385,240 460,240Q529,240 580,285.5Q631,331 639,400L558,400Q551,365 523.5,342.5Q496,320 460,320Q418,320 389,349Q360,378 360,420Q360,440 368,459.5Q376,479 391,494L445,548Q459,562 467.5,578.5Q476,595 482,612Q487,625 497,632.5Q507,640 520,640Q537,640 548.5,628.5Q560,617 560,600L640,600Q640,650 605.5,685Q571,720 520,720ZM540,560Q515,560 497.5,542.5Q480,525 480,500Q480,474 497.5,457Q515,440 540,440Q566,440 583,457Q600,474 600,500Q600,525 583,542.5Q566,560 540,560Z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/shelf_action_chip_background.xml b/packages/SystemUI/res/drawable/shelf_action_chip_background.xml
new file mode 100644
index 0000000..63600be
--- /dev/null
+++ b/packages/SystemUI/res/drawable/shelf_action_chip_background.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:color="@color/overlay_button_ripple">
+ <item android:id="@android:id/background">
+ <shape android:shape="rectangle">
+ <solid android:color="?androidprv:attr/materialColorSecondary"/>
+ <corners android:radius="10000dp"/> <!-- fully-rounded radius -->
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout/auth_biometric_icon.xml b/packages/SystemUI/res/drawable/shelf_action_chip_container_background.xml
similarity index 67%
rename from packages/SystemUI/res/layout/auth_biometric_icon.xml
rename to packages/SystemUI/res/drawable/shelf_action_chip_container_background.xml
index b2df63d..bb8cece 100644
--- a/packages/SystemUI/res/layout/auth_biometric_icon.xml
+++ b/packages/SystemUI/res/drawable/shelf_action_chip_container_background.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,13 +14,10 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-
-<com.airbnb.lottie.LottieAnimationView
+<shape
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/biometric_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:contentDescription="@null"
- android:scaleType="fitXY"/>
\ No newline at end of file
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/materialColorSurfaceBright"/>
+ <corners android:radius="10000dp"/> <!-- fully-rounded radius -->
+</shape>
diff --git a/packages/SystemUI/res/drawable/shelf_action_chip_divider.xml b/packages/SystemUI/res/drawable/shelf_action_chip_divider.xml
new file mode 100644
index 0000000..a5b44e5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/shelf_action_chip_divider.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android = "http://schemas.android.com/apk/res/android">
+ <size
+ android:width = "@dimen/overlay_action_chip_margin_start"
+ android:height = "0dp"/>
+</shape>
diff --git a/packages/SystemUI/res/layout/auth_biometric_icon.xml b/packages/SystemUI/res/drawable/shelf_action_container_clipping_shape.xml
similarity index 67%
copy from packages/SystemUI/res/layout/auth_biometric_icon.xml
copy to packages/SystemUI/res/drawable/shelf_action_container_clipping_shape.xml
index b2df63d..76779f9 100644
--- a/packages/SystemUI/res/layout/auth_biometric_icon.xml
+++ b/packages/SystemUI/res/drawable/shelf_action_container_clipping_shape.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,13 +14,10 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-
-<com.airbnb.lottie.LottieAnimationView
+<shape
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/biometric_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:contentDescription="@null"
- android:scaleType="fitXY"/>
\ No newline at end of file
+ android:shape="rectangle">
+ <!-- We don't actually draw anything, just expressing the shape for clipping. -->
+ <solid android:color="#0000"/>
+ <corners android:radius="10000dp"/> <!-- fully-rounded radius -->
+</shape>
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index 13355f3..76d10cc 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -47,7 +47,7 @@
android:layout_marginBottom="@dimen/bluetooth_dialog_layout_margin"
android:ellipsize="end"
android:gravity="center_vertical|center_horizontal"
- android:maxLines="1"
+ android:maxLines="2"
android:text="@string/quick_settings_bluetooth_tile_subtitle"
android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
app:layout_constraintEnd_toEndOf="parent"
@@ -256,6 +256,24 @@
app:constraint_referenced_ids="pair_new_device_button,bluetooth_auto_on_toggle_info_text" />
<Button
+ android:id="@+id/audio_sharing_button"
+ style="@style/Widget.Dialog.Button.BorderButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="9dp"
+ android:layout_marginBottom="@dimen/dialog_bottom_padding"
+ android:layout_marginEnd="@dimen/dialog_side_padding"
+ android:layout_marginStart="@dimen/dialog_side_padding"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="@string/quick_settings_bluetooth_audio_sharing_button"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/barrier"
+ app:layout_constraintVertical_bias="1"
+ android:visibility="gone" />
+
+ <Button
android:id="@+id/done_button"
style="@style/Widget.Dialog.Button"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 1eb05bf..e3c5a7d 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -36,8 +36,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
- android:focusable="true"
- android:accessibilityTraversalBefore="@android:id/edit"
+ android:focusable="false"
+ android:importantForAccessibility="yes"
android:clipToPadding="false"
android:clipChildren="false">
diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml
index eeb64bd8..6a5b999f 100644
--- a/packages/SystemUI/res/layout/screenshot_shelf.xml
+++ b/packages/SystemUI/res/layout/screenshot_shelf.xml
@@ -20,39 +20,37 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ImageView
+ <FrameLayout
android:id="@+id/actions_container_background"
android:visibility="gone"
- android:layout_height="0dp"
- android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
android:elevation="4dp"
- android:background="@drawable/action_chip_container_background"
- android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
+ android:background="@drawable/shelf_action_chip_container_background"
+ android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
android:layout_marginBottom="@dimen/screenshot_shelf_vertical_margin"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/actions_container"
- app:layout_constraintEnd_toEndOf="@+id/actions_container"
- app:layout_constraintBottom_toTopOf="@id/guideline"/>
- <HorizontalScrollView
- android:id="@+id/actions_container"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
- android:paddingHorizontal="@dimen/overlay_action_container_padding_end"
- android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
- android:elevation="4dp"
- android:scrollbars="none"
- app:layout_constraintHorizontal_bias="0"
- app:layout_constraintWidth_percent="1.0"
- app:layout_constraintWidth_max="wrap"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="@id/actions_container_background">
- <LinearLayout
- android:id="@+id/screenshot_actions"
+ app:layout_constraintBottom_toTopOf="@id/guideline"
+ >
+ <HorizontalScrollView
+ android:id="@+id/actions_container"
android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </HorizontalScrollView>
+ android:layout_height="wrap_content"
+ android:layout_marginVertical="@dimen/overlay_action_container_padding_vertical"
+ android:layout_marginHorizontal="@dimen/overlay_action_chip_margin_start"
+ android:background="@drawable/shelf_action_container_clipping_shape"
+ android:clipToOutline="true"
+ android:scrollbars="none">
+ <LinearLayout
+ android:id="@+id/screenshot_actions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:showDividers="middle"
+ android:divider="@drawable/shelf_action_chip_divider"
+ android:animateLayoutChanges="true"
+ />
+ </HorizontalScrollView>
+ </FrameLayout>
<View
android:id="@+id/screenshot_preview_border"
android:layout_width="0dp"
@@ -66,7 +64,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/screenshot_preview"
app:layout_constraintEnd_toEndOf="@id/screenshot_preview"
- app:layout_constraintBottom_toTopOf="@id/actions_container"/>
+ app:layout_constraintBottom_toTopOf="@id/actions_container_background"/>
<ImageView
android:id="@+id/screenshot_preview"
android:layout_width="@dimen/overlay_x_scale"
diff --git a/packages/SystemUI/res/layout/shelf_action_chip.xml b/packages/SystemUI/res/layout/shelf_action_chip.xml
new file mode 100644
index 0000000..c7606e4
--- /dev/null
+++ b/packages/SystemUI/res/layout/shelf_action_chip.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:theme="@style/FloatingOverlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingVertical="@dimen/overlay_action_chip_padding_vertical"
+ android:gravity="center"
+ android:background="@drawable/shelf_action_chip_background"
+ >
+ <ImageView
+ android:id="@+id/overlay_action_chip_icon"
+ android:tint="?androidprv:attr/materialColorOnSecondary"
+ android:layout_width="@dimen/overlay_action_chip_icon_size"
+ android:layout_height="@dimen/overlay_action_chip_icon_size"/>
+ <TextView
+ android:id="@+id/overlay_action_chip_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:textSize="@dimen/overlay_action_chip_text_size"
+ android:textColor="?androidprv:attr/materialColorOnSecondary"/>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index 9b1fa23..8f1e061 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -67,8 +67,8 @@
android:layout_width="@dimen/volume_ringer_drawer_icon_size"
android:layout_height="@dimen/volume_ringer_drawer_icon_size"
android:layout_gravity="center"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/ic_volume_ringer_vibrate" />
+ android:src="@drawable/ic_volume_ringer_vibrate"
+ android:tint="?android:attr/textColorPrimary" />
</FrameLayout>
@@ -76,6 +76,7 @@
android:id="@+id/volume_drawer_mute"
android:layout_width="@dimen/volume_ringer_drawer_item_size"
android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:accessibilityTraversalAfter="@id/volume_drawer_vibrate"
android:contentDescription="@string/volume_ringer_hint_mute"
android:gravity="center">
@@ -84,8 +85,8 @@
android:layout_width="@dimen/volume_ringer_drawer_icon_size"
android:layout_height="@dimen/volume_ringer_drawer_icon_size"
android:layout_gravity="center"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/ic_speaker_mute" />
+ android:src="@drawable/ic_speaker_mute"
+ android:tint="?android:attr/textColorPrimary" />
</FrameLayout>
@@ -93,6 +94,7 @@
android:id="@+id/volume_drawer_normal"
android:layout_width="@dimen/volume_ringer_drawer_item_size"
android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:accessibilityTraversalAfter="@id/volume_drawer_mute"
android:contentDescription="@string/volume_ringer_hint_unmute"
android:gravity="center">
@@ -101,8 +103,8 @@
android:layout_width="@dimen/volume_ringer_drawer_icon_size"
android:layout_height="@dimen/volume_ringer_drawer_icon_size"
android:layout_gravity="center"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/ic_speaker_on" />
+ android:src="@drawable/ic_speaker_on"
+ android:tint="?android:attr/textColorPrimary" />
</FrameLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 2027d16..0e1bed8 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -71,7 +71,7 @@
<string name="usb_port_enabled" msgid="531823867664717018">"USB-poort is geaktiveer om laaiers en bykomstighede te bespeur"</string>
<string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"Aktiveer USB"</string>
<string name="learn_more" msgid="4690632085667273811">"Kom meer te wete"</string>
- <string name="global_action_screenshot" msgid="2760267567509131654">"Skermkiekie"</string>
+ <string name="global_action_screenshot" msgid="2760267567509131654">"Skermskoot"</string>
<string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"Hou Ontsluit is gedeaktiveer"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"het \'n prent gestuur"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Stoor tans skermkiekie..."</string>
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Sien alles"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Gebruik Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Gekoppel"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gestoor"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ontkoppel"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveer"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Skakel dit môre outomaties weer aan"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Kenmerke soos Kitsdeel, Kry My Toestel en toestelligging gebruik Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth sal môre om 05:00 aanskakel"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterykrag"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Neem kwessie op"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Begin"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Foutverslag"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Watter deel van jou toestelervaring is geraak?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Kies soort kwessie"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skermopname"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standaard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Medium"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Hoog"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Gehoortoestelle"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Gehoortoestelle"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bind nuwe toestel saam"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nuwe toestel saam te bind"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokkeer toestelmikrofoon?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokkeer toestelkamera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokkeer toestelkamera en mikrofoon?"</string>
@@ -448,10 +453,8 @@
<string name="button_to_remove_widget" msgid="3948204829181214098">"Verwyder"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Voeg legstuk by"</string>
<string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Klaar"</string>
- <!-- no translation found for label_for_button_in_empty_state_cta (7314975555382055823) -->
- <skip />
- <!-- no translation found for title_for_empty_state_cta (6161654421223450530) -->
- <skip />
+ <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Voeg legstukke by"</string>
+ <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Kry kitstoegang tot jou gunstelingapplegstukke sonder om jou tablet te ontsluit."</string>
<string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Laat enige legstuk op die sluitskerm toe?"</string>
<string name="button_text_to_open_settings" msgid="1987729256950941628">"Maak instellings oop"</string>
<string name="work_mode_off_title" msgid="5794818421357835873">"Hervat werkapps?"</string>
@@ -747,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Wissel sleutelborduitleg"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"of"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Maak soeknavraag skoon"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Kortpaaie"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Kortpadsleutels"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Soek kortpaaie"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Geen kortpaaie gevind nie"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Stelsel"</string>
@@ -772,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Maak Assistent oop"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Sluit skerm"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Maak ’n nota"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Verrig veelvuldige stelseltake"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Gaan by verdeelde skerm in met huidige app aan die regterkant"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Gaan by verdeelde skerm in met huidige app aan die linkerkant"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Verrigting van veelvuldige take"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gebruik verdeelde skerm met huidige app aan die regterkant"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gebruik verdeelde skerm met huidige app aan die linkerkant"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Skakel oor van verdeelde skerm na volskerm"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skakel oor na app regs of onder terwyl jy verdeelde skerm gebruik"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skakel oor na app links of bo terwyl jy verdeelde skerm gebruik"</string>
@@ -895,12 +898,12 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Opletberigte"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Skermkiekies"</string>
- <string name="notification_channel_instant" msgid="7556135423486752680">"Kitsprogramme"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Kitsapps"</string>
<string name="notification_channel_setup" msgid="7660580986090760350">"Opstelling"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Berging"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Wenke"</string>
<string name="notification_channel_accessibility" msgid="8956203986976245820">"Toeganklikheid"</string>
- <string name="instant_apps" msgid="8337185853050247304">"Kitsprogramme"</string>
+ <string name="instant_apps" msgid="8337185853050247304">"Kitsapps"</string>
<string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> loop tans"</string>
<string name="instant_apps_message" msgid="6112428971833011754">"Program is oopgemaak sonder dat dit geïnstalleer is."</string>
<string name="instant_apps_message_with_help" msgid="1816952263531203932">"Program is oopgemaak sonder dat dit geïnstalleer is. Tik om meer te wete te kom."</string>
@@ -1195,8 +1198,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kies gebruiker"</string>
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app is aktief}other{# apps is aktief}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nuwe inligting"</string>
- <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiewe programme"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Hierdie programme is aktief en werk, selfs wanneer jy hulle nie gebruik nie. Dit verbeter hul funksies, maar beïnvloed dalk ook batterylewe."</string>
+ <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiewe apps"</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Hierdie apps is aktief en werk, selfs wanneer jy hulle nie gebruik nie. Dit verbeter hul funksies, maar beïnvloed dalk ook batterylewe."</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Gestop"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Klaar"</string>
diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml
index 8f0532e..1b4781d 100644
--- a/packages/SystemUI/res/values-af/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Af"</item>
<item msgid="5137565285664080143">"Aan"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Nie beskikbaar nie"</item>
+ <item msgid="3079622119444911877">"Af"</item>
+ <item msgid="3028994095749238254">"Aan"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index a3700f2..3709a900 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ሁሉንም ይመልከቱ"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ብሉቱዝን ይጠቀሙ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ተገናኝቷል"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ተቀምጧል"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ግንኙነትን አቋርጥ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ያግብሩ"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ነገ እንደገና በራስ-ሰር አስጀምር"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"እንደ ፈጣን ማጋራት፣ የእኔን መሣሪያ አግኝ እና የመሣሪያ አካባቢ ያሉ ባህሪያት ብሉቱዝን ይጠቀማሉ"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ብሉቱዝ ነገ ጠዋቱ 5 ሰዓት ላይ ይበራል"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ባትሪ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ኦዲዮ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ማዳመጫ"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"ችግርን ቅዳ"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"ጀምር"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"አቁም"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"የሳንካ ሪፖርት"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"የትኛው የመሣሪያዎ ተሞክሮ ክፍል ተጎድቷል?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"የችግሩን አይነት ይምረጡ"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"የማያ መቅረጫ"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"መካከለኛ"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"ከፍተኛ"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"የመስሚያ መሣሪያዎች"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"የመስማት ችሎታ መሣሪያ"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"አዲስ መሣሪያ ያጣምሩ"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"አዲስ መሣሪያ ለማጣመር ጠቅ ያድርጉ"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"የመሣሪያ ማይክሮፎን እገዳ ይነሳ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"የመሣሪያ ካሜራ እገዳ ይነሳ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"የመሣሪያ ካሜራ እና ማይክሮፎን እገዳ ይነሳ?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"የቁልፍ ሰሌዳ ገጽታ ለውጥ"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ወይም"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"የፍለጋ መጠይቅን አጽዳ"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"አቋራጮች"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"የቁልፍ ሰሌዳ አቋራጮች"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"አቋራጮችን ይፈልጉ"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ምንም አቋራጮች አልተገኙም"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"ሥርዓት"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"ረዳትን ክፈት"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ማያ ገፅ ቁልፍ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ማስታወሻ ይውሰዱ"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"የሥርዓት ብዙ ተግባራትን በተመሳሳይ ጊዜ ማከናወን"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"ለአርኤችኤስ በአሁኑ መተግበሪያ ወደ የተከፈለ ማያ ገጽ ግባ"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"ለኤልኤችኤስ በአሁኑ መተግበሪያ ወደ የተከፈለ ማያ ገጽ ይግቡ"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ብዙ ተግባራትን በተመሳሳይ ጊዜ ማከናወን"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"የአሁኑ መተግበሪያ በስተቀኝ ላይ ሆኖ የተከፈለ ማያ ገጽን ይጠቀሙ"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"የአሁኑ መተግበሪያ በስተግራ ላይ ሆኖ የተከፈለ ማያ ገጽን ይጠቀሙ"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"ከየተከፈለ ማያ ገጽ ወደ ሙሉ ገጽ ዕይታ ቀይር"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"የተከፈለ ማያ ገጽን ሲጠቀሙ በቀኝ ወይም ከታች ወዳለ መተግበሪያ ይቀይሩ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"የተከፈለ ማያ ገጽን ሲጠቀሙ በቀኝ ወይም ከላይ ወዳለ መተግበሪያ ይቀይሩ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 767e909..0a7250a 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -75,7 +75,7 @@
<string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"تم إيقاف ميزة \"إبقاء الجهاز مفتوحًا\"."</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"أرسَل صورة"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"جارٍ حفظ لقطة الشاشة..."</string>
- <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"جارٍ حفظ لقطة الشاشة في الملف الشخصي للعمل…"</string>
+ <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"جارٍ حفظ لقطة الشاشة في ملف العمل…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"تم حفظ لقطة الشاشة."</string>
<string name="screenshot_failed_title" msgid="3259148215671936891">"تعذّر حفظ لقطة الشاشة"</string>
<string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"الشاشة الخارجية"</string>
@@ -90,13 +90,13 @@
<string name="screenshot_share_description" msgid="2861628935812656612">"مشاركة لقطة الشاشة"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"التقاط المزيد من المحتوى"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"إغلاق لقطة الشاشة"</string>
- <string name="screenshot_dismiss_work_profile" msgid="3101530842987697045">"تجاهل رسالة الملف الشخصي للعمل"</string>
+ <string name="screenshot_dismiss_work_profile" msgid="3101530842987697045">"تجاهل رسالة ملف العمل"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"معاينة لقطة الشاشة"</string>
<string name="screenshot_top_boundary_pct" msgid="2520148599096479332">"الحد العلوي <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"الحد السفلى <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"الحد الأيسر <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
<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_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>
@@ -120,7 +120,7 @@
<string name="screenrecord_stop_label" msgid="72699670052087989">"إيقاف"</string>
<string name="screenrecord_share_label" msgid="5025590804030086930">"مشاركة"</string>
<string name="screenrecord_save_title" msgid="1886652605520893850">"تم حفظ تسجيل الشاشة"</string>
- <string name="screenrecord_save_text" msgid="3008973099800840163">"انقر لعرض التسجيل."</string>
+ <string name="screenrecord_save_text" msgid="3008973099800840163">"انقر لعرض التسجيل"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"حدث خطأ أثناء حفظ تسجيل محتوى الشاشة."</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"حدث خطأ في بدء تسجيل الشاشة"</string>
<string name="issuerecord_title" msgid="286627115110121849">"مسجّلة المشاكل"</string>
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"عرض الكل"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"استخدام البلوتوث"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"متّصل"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"إلغاء الربط"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"تفعيل"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"تفعيل البلوتوث تلقائيًا مرة أخرى غدًا"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"يُستخدَم البلوتوث في ميزات مثل Quick Share و\"العثور على جهازي\" والموقع الجغرافي للجهاز"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"سيتم تفعيل البلوتوث غدًا الساعة 5 صباحًا"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string>
@@ -287,7 +295,7 @@
<string name="quick_settings_location_label" msgid="2621868789013389163">"الموقع الجغرافي"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"شاشة الاستراحة"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"الوصول إلى الكاميرا"</string>
- <string name="quick_settings_mic_label" msgid="8392773746295266375">"الوصول إلى الميكروفون"</string>
+ <string name="quick_settings_mic_label" msgid="8392773746295266375">"الوصول للميكروفون"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"متاح"</string>
<string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"محظور"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"جهاز الوسائط"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"تسجيل المشكلة"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"بدء"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"إيقاف"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"تقرير خطأ"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ما هو الجانب الذي تأثّر في تجربة استخدام الجهاز؟"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"اختيار نوع المشكلة"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"تسجيل الشاشة"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"عادي"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"متوسط"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"مرتفع"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعات الأذن الطبية"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سماعات الأذن الطبية"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"إقران جهاز جديد"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"انقر لإقران جهاز جديد"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"هل تريد إزالة حظر ميكروفون الجهاز؟"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"هل تريد إزالة حظر كاميرا الجهاز؟"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"هل تريد إزالة حظر الكاميرا والميكروفون؟"</string>
@@ -518,9 +523,9 @@
<string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>."</string>
<string name="quick_settings_disclosure_management_vpns" msgid="929181757984262902">"ينتمي هذا الجهاز إلى مؤسستك، ويتّصل بالإنترنت من خلال خدمات الشبكة الافتراضية الخاصة (VPN)."</string>
<string name="quick_settings_disclosure_named_management_vpns" msgid="3312645578322079185">"ينتمي هذا الجهاز إلى <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>، ويتّصل بالإنترنت من خلال خدمات الشبكة الافتراضية الخاصة (VPN)."</string>
- <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"يمكن لمؤسستك مراقبة حركة بيانات الشبكة في الملف الشخصي للعمل"</string>
+ <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"يمكن لمؤسستك مراقبة حركة بيانات الشبكة في ملف العمل"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"يمكن لـ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> مراقبة حركة بيانات الشبكة في ملفك الشخصي للعمل"</string>
- <string name="quick_settings_disclosure_managed_profile_network_activity" msgid="2636594621387832827">"تكون أنشطة شبكة الملف الشخصي للعمل مرئية لمشرف تكنولوجيا المعلومات."</string>
+ <string name="quick_settings_disclosure_managed_profile_network_activity" msgid="2636594621387832827">"تكون أنشطة شبكة ملف العمل مرئية لمشرف تكنولوجيا المعلومات."</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"قد تكون الشبكة خاضعة للمراقبة"</string>
<string name="quick_settings_disclosure_vpns" msgid="3586175303518266301">"هذا الجهاز متّصل بالإنترنت من خلال خدمات الشبكات الافتراضية الخاصة (VPN)."</string>
<string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="153393105176944100">"تطبيقات العمل الخاصة بك متّصلة بالإنترنت من خلال <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
@@ -639,7 +644,7 @@
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"إعدادات شاشة القفل"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"ماسح ضوئي لرمز الاستجابة السريعة"</string>
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"جارٍ تعديل الحالة"</string>
- <string name="status_bar_work" msgid="5238641949837091056">"الملف الشخصي للعمل"</string>
+ <string name="status_bar_work" msgid="5238641949837091056">"ملف العمل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"لن تسمع المنبّه القادم في <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"في <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -649,7 +654,7 @@
<string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"قمر صناعي، الاتصال ضعيف"</string>
<string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"قمر صناعي، الاتصال جيد"</string>
<string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"قمر صناعي، الاتصال متوفّر"</string>
- <string name="accessibility_managed_profile" msgid="4703836746209377356">"الملف الشخصي للعمل"</string>
+ <string name="accessibility_managed_profile" msgid="4703836746209377356">"ملف العمل"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"متعة للبعض وليس للجميع"</string>
<string name="tuner_warning" msgid="1861736288458481650">"توفر لك أداة ضبط واجهة مستخدم النظام طرقًا إضافية لتعديل واجهة مستخدم Android وتخصيصها. ويمكن أن تطرأ تغييرات على هذه الميزات التجريبية أو يمكن أن تتعطل هذه الميزات أو تختفي في الإصدارات المستقبلية. عليك متابعة الاستخدام مع توخي الحذر."</string>
<string name="tuner_persistent_warning" msgid="230466285569307806">"يمكن أن تطرأ تغييرات على هذه الميزات التجريبية أو يمكن أن تتعطل هذه الميزات أو تختفي في الإصدارات المستقبلية. عليك متابعة الاستخدام مع توخي الحذر."</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"تبديل تنسيق لوحة المفاتيح"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"أو"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"محو طلب البحث"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"الاختصارات"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"اختصارات لوحة المفاتيح"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"البحث في الاختصارات"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"لم يُعثَر على اختصارات."</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"النظام"</string>
@@ -770,14 +775,14 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"فتح \"مساعد Google\""</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"شاشة القفل"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"تدوين ملاحظة"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"تعدُّد المهام في النظام"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"تفعيل وضع \"تقسيم الشاشة\" مع عرض التطبيق الحالي على يسار الشاشة"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"تفعيل وضع \"تقسيم الشاشة\" مع عرض التطبيق الحالي على يمين الشاشة"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"تعدُّد المهام"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"استخدام \"وضع تقسيم الشاشة\" مع تثبيت التطبيق الحالي على اليمين"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"استخدام \"وضع تقسيم الشاشة\" مع تثبيت التطبيق الحالي على اليسار"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"التبديل من وضع \"تقسيم الشاشة\" إلى وضع \"ملء الشاشة\""</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"التبديل إلى التطبيق على اليسار أو الأسفل أثناء استخدام \"تقسيم الشاشة\""</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"التبديل إلى التطبيق على اليمين أو الأعلى أثناء استخدام \"تقسيم الشاشة\""</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"استبدال تطبيق بآخر في وضع \"تقسيم الشاشة\""</string>
- <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"إدخال"</string>
+ <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"الإدخال"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"التبديل إلى اللغة التالية"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"التبديل إلى اللغة السابقة"</string>
<string name="input_access_emoji" msgid="8105642858900406351">"الوصول إلى الرموز التعبيرية"</string>
@@ -789,7 +794,7 @@
<string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"البريد الإلكتروني"</string>
<string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"الرسائل القصيرة SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"الموسيقى"</string>
- <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"التقويم"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"تقويم Google"</string>
<string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"الآلة الحاسبة"</string>
<string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"خرائط Google"</string>
<string name="volume_and_do_not_disturb" msgid="502044092739382832">"عدم الإزعاج"</string>
@@ -802,7 +807,7 @@
<string name="data_saver" msgid="3484013368530820763">"توفير البيانات"</string>
<string name="accessibility_data_saver_on" msgid="5394743820189757731">"تم تفعيل توفير البيانات"</string>
<string name="switch_bar_on" msgid="1770868129120096114">"مفعّل"</string>
- <string name="switch_bar_off" msgid="5669805115416379556">"متوقف"</string>
+ <string name="switch_bar_off" msgid="5669805115416379556">"غير مفعّل"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"غير متوفّر"</string>
<string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"مزيد من المعلومات"</string>
<string name="nav_bar" msgid="4642708685386136807">"شريط التنقل"</string>
@@ -830,7 +835,7 @@
<string name="left_icon" msgid="5036278531966897006">"رمز اليسار"</string>
<string name="right_icon" msgid="1103955040645237425">"رمز اليمين"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"اضغط باستمرار مع السحب لإضافة المربّعات"</string>
- <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"اضغط باستمرار مع السحب لإعادة ترتيب الميزات."</string>
+ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"اضغط باستمرار مع السحب لإعادة ترتيب الميزات"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"اسحب هنا للإزالة"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"الحدّ الأدنى من عدد المربعات الذي تحتاج إليه هو <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"تعديل"</string>
@@ -995,7 +1000,7 @@
<string name="accessibility_floating_button_undo" msgid="511112888715708241">"تراجع"</string>
<string name="accessibility_floating_button_hidden_notification_title" msgid="4115036997406994799">"زر أدوات تسهيل الاستخدام مخفي"</string>
<string name="accessibility_floating_button_hidden_notification_text" msgid="1457021647040915658">"انقر لإظهار زر أدوات تسهيل الاستخدام."</string>
- <string name="accessibility_floating_button_undo_message_label_text" msgid="9017658016426242640">"تمت إزالة اختصار <xliff:g id="FEATURE_NAME">%s</xliff:g>."</string>
+ <string name="accessibility_floating_button_undo_message_label_text" msgid="9017658016426242640">"تمت إزالة اختصار ميزة \"<xliff:g id="FEATURE_NAME">%s</xliff:g>\""</string>
<string name="accessibility_floating_button_undo_message_number_text" msgid="4909270290725226075">"{count,plural, =1{تمت إزالة اختصار واحد.}zero{تمت إزالة # اختصار.}two{تمت إزالة اختصارَين.}few{تمت إزالة # اختصارات.}many{تمت إزالة # اختصارًا.}other{تمت إزالة # اختصار.}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"النقل إلى أعلى يمين الشاشة"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"النقل إلى أعلى يسار الشاشة"</string>
@@ -1265,8 +1270,8 @@
<string name="video_camera" msgid="7654002575156149298">"كاميرا فيديو"</string>
<string name="call_from_work_profile_title" msgid="5418253516453177114">"لا يمكن الاتصال من تطبيق شخصي"</string>
<string name="call_from_work_profile_text" msgid="2856337395968118274">"تسمح لك مؤسستك بإجراء المكالمات من تطبيقات العمل فقط."</string>
- <string name="call_from_work_profile_action" msgid="2937701298133010724">"التبديل إلى الملف الشخصي للعمل"</string>
- <string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"تثبيت تطبيق الهاتف في الملف الشخصي للعمل"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"التبديل إلى ملف العمل"</string>
+ <string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"تثبيت تطبيق الهاتف في ملف العمل"</string>
<string name="call_from_work_profile_close" msgid="5830072964434474143">"إلغاء"</string>
<string name="lock_screen_settings" msgid="6152703934761402399">"تخصيص شاشة القفل"</string>
<string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"الفتح لتخصيص شاشة القفل"</string>
diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
index 307a26e..a89650a 100644
--- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"الخيار غير مفعَّل"</item>
<item msgid="5137565285664080143">"الخيار مفعَّل"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"غير متوفّرة"</item>
+ <item msgid="3079622119444911877">"غير مفعَّلة"</item>
+ <item msgid="3028994095749238254">"مفعَّلة"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 5a083ab..3311588 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"আটাইবোৰ চাওক"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ব্লুটুথ ব্যৱহাৰ কৰক"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"সংযুক্ত আছে"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ছেভ কৰা হৈছে"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"সংযোগ বিচ্ছিন্ন কৰক"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"সক্ৰিয় কৰক"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"কাইলৈ পুনৰ স্বয়ংক্ৰিয়ভাৱে অন কৰক"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, Find My Device আৰু ডিভাইচৰ অৱস্থানৰ দৰে সুবিধাই ব্লুটুথ ব্যৱহাৰ কৰে"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"কাইলৈ পুৱা ৫ বজাত ব্লুটুথ অন হ’ব"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"ৰেকৰ্ড সম্পৰ্কীয় সমস্যা"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"আৰম্ভ কৰক"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"বন্ধ কৰক"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"বাগ ৰিপ’ৰ্ট"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"আপোনাৰ ডিভাইচৰ অভিজ্ঞতাৰ কোনটো অংশ প্ৰভাৱিত হৈছিল?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"সমস্যাৰ প্ৰকাৰ বাছনি কৰক"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"স্ক্ৰীন ৰেকৰ্ড"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"মধ্যমীয়া"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"উচ্চ"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"শুনাৰ ডিভাইচ"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"শুনাৰ ডিভাইচ"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"নতুন ডিভাইচ পেয়াৰ কৰক"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইচ পেয়াৰ কৰিবলৈ ক্লিক কৰক"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইচৰ মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইচৰ কেমেৰা অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইচৰ কেমেৰা আৰু মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"কীব\'ৰ্ডৰ সজ্জা সলনি কৰক"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"অথবা"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"সন্ধান কৰা প্ৰশ্ন মচক"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"শ্বৰ্টকাট"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"কীব’ৰ্ডৰ শ্বৰ্টকাট"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"সন্ধানৰ শ্বৰ্টকাট"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"কোনো শ্বৰ্টকাট বিচাৰি পোৱা নাই"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"ছিষ্টেম"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant খোলক"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"লক স্ক্ৰীন"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"টোকা লিখক"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ছিষ্টেম মাল্টিটাস্কিং"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"বৰ্তমানৰ এপৰ জৰিয়তে বিভাজিত স্ক্ৰীনৰ সোঁফালৰ স্ক্ৰীনখনত সোমাওক"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"বৰ্তমানৰ এপৰ জৰিয়তে বিভাজিত স্ক্ৰীনৰ বাওঁফালৰ স্ক্ৰীনখনত সোমাওক"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"মাল্টিটাস্কিং"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"বৰ্তমানৰ এপ্টোৰ সৈতে সোঁফালে বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰক"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"বৰ্তমানৰ এপ্টোৰ সৈতে বাওঁফালে বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰক"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"বিভাজিত স্ক্ৰীনৰ পৰা পূৰ্ণ স্ক্ৰীনলৈ সলনি কৰক"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰাৰ সময়ত সোঁফালে অথবা তলত থকা এপলৈ সলনি কৰক"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰাৰ সময়ত বাওঁফালে অথবা ওপৰত থকা এপলৈ সলনি কৰক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 7faec8a..741cc41 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Hamısına baxın"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth aç"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Qoşulub"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Yadda saxlandı"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"əlaqəni kəsin"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivləşdirin"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Sabah avtomatik aktiv edin"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Cəld Paylaşım, Cihazın Tapılması və cihaz məkanı kimi funksiyalar Bluetooth istifadə edir"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth sabah 05:00-da aktiv olacaq"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batareya"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Qeyd problemi"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Başlayın"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Dayandırın"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Baq hesabatı"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz istifadəsinə necə təsir etdi?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problem növü seçin"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran qeydəalma"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standart"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Orta"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Yüksək"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eşitmə cihazları"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Eşitmə cihazları"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz birləşdirin"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz birləşdirmək üçün klikləyin"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonu blokdan çıxarılsın?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerası blokdan çıxarılsın?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası və mikrofonu blokdan çıxarılsın?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Klaviatura düzümünü dəyişin"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"və ya"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Axtarış sorğusunu silin"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Qısayollar"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Klaviatura qısayolları"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Qısayollar axtarın"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Qısayol tapılmadı"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistem"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistenti açın"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kilid ekranı"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Qeyd götürün"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Sistemdə çoxsaylı tapşırıq icrası"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Cari tətbiq sağda olmaqla bölünmüş ekrana daxil olun"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Cari tətbiq solda olmaqla bölünmüş ekrana daxil olun"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Çoxsaylı tapşırıq icrası"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Cari tətbiq sağda olmaqla bölünmüş ekrandan istifadə edin"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Cari tətbiq solda olmaqla bölünmüş ekrandan istifadə edin"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Bölünmüş ekrandan tam ekrana keçin"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bölünmüş ekran istifadə edərkən sağda və ya aşağıda tətbiqə keçin"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bölünmüş ekran istifadə edərkən solda və ya yuxarıda tətbiqə keçin"</string>
diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml
index f390369..c24f402 100644
--- a/packages/SystemUI/res/values-az/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Deaktiv"</item>
<item msgid="5137565285664080143">"Aktiv"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Əlçatan deyil"</item>
+ <item msgid="3079622119444911877">"Deaktiv"</item>
+ <item msgid="3028994095749238254">"Aktiv"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index cd5dc26..583ffaf 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Prikaži sve"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinite vezu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivirajte"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcije kao što su Quick Share, Pronađi moj uređaj i lokacija uređaja koriste Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth će se uključiti sutra u 5:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Evidentirajte problem"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Pokreni"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavi"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izveštaj o grešci"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji deo doživljaja na uređaju je ovo uticalo?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izaberite tip problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Srednje"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Visoko"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni aparati"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Upari novi uređaj"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili nov uređaj"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite da odblokirate mikrofon uređaja?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite da odblokirate kameru uređaja?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite da odblokirate kameru i mikrofon uređaja?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Promeni raspored tastature"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ili"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Obriši upit za pretragu"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Prečice"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Tasterske prečice"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pretražite prečice"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nisu pronađene prečice"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistem"</string>
@@ -768,10 +774,10 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvori podešavanja"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvori pomoćnika"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zaključavanje ekrana"</string>
- <string name="group_system_quick_memo" msgid="3764560265935722903">"Napravite belešku"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Obavljanje više zadataka sistema istovremeno"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Pokreni podeljeni ekran za aktuelnu aplikaciju na desnoj strani"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Pokreni podeljeni ekran za aktuelnu aplikaciju na levoj strani"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Napravi belešku"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Obavljanje više zadataka istovremeno"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Koristite podeljeni ekran sa aktuelnom aplikacijom s desne strane"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Koristite podeljeni ekran sa aktuelnom aplikacijom s leve strane"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Pređi sa podeljenog ekrana na ceo ekran"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pređite u aplikaciju zdesna ili ispod dok koristite podeljeni ekran"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju sleva ili iznad dok koristite podeljeni ekran"</string>
@@ -1295,9 +1301,9 @@
<string name="privacy_dialog_manage_permissions" msgid="2543451567190470413">"Upravljaj pristupom"</string>
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"Koristi telefonski poziv"</string>
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"Nedavno korišćeno u telefonskom pozivu"</string>
- <string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Koriste <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Koristi <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Nedavno koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Koriste <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">"Koristi <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">"Nedavno koristila aplikacija <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">"Koriste <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">"Nedavno koristila aplikacija <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-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index a76c38b..50b407f 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Паглядзець усе"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Выкарыстоўваць Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Падключана"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Захавана"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"адключыць"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"актываваць"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аўтаматычнае ўключэнне заўтра"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Bluetooth выкарыстоўваецца для вызначэння месцазнаходжання прылады, а таксама такімі функцыямі, як Хуткае абагульванне і Знайсці прыладу"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth будзе ўключаны заўтра ў 5 гадзін раніцы"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Узровень зараду: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Гук"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Запіс праблемы"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Пачынайце"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Спыніцеся"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Справаздача"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"З чым была звязана праблема, якая вам сустрэлася?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Выберыце тып праблемы"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запіс экрана"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандартная"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Сярэдняя"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Высокая"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слыхавыя апараты"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слыхавыя апараты"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спалучыць новую прыладу"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Націсніце, каб спалучыць новую прыладу"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблакіраваць мікрафон прылады?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблакіраваць камеру прылады?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблакіраваць камеру і мікрафон прылады?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Пераключыць раскладку клавіятуры"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"або"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Ачысціць пошукавы запыт"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Ярлыкі"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Спалучэнні клавіш"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Пошук ярлыкоў"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ярлыкі не знойдзены"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Сістэма"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Выклікаць Памочніка"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Экран блакіроўкі"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Стварыць нататку"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Шматзадачнасць сістэмы"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Перайсці ў рэжым падзеленага экрана з бягучай праграмай справа"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Перайсці ў рэжым падзеленага экрана з бягучай праграмай злева"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Шматзадачнасць"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Падзяліць экран і памясціць гэту праграму справа"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Падзяліць экран і памясціць гэту праграму злева"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Пераключыцца з рэжыму падзеленага экрана на поўнаэкранны рэжым"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Пераключыцца на праграму справа або ўнізе на падзеленым экране"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пераключыцца на праграму злева або ўверсе на падзеленым экране"</string>
diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml
index 32619ef..33e704c 100644
--- a/packages/SystemUI/res/values-be/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Выключана"</item>
<item msgid="5137565285664080143">"Уключана"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Недаступна"</item>
+ <item msgid="3079622119444911877">"Выключана"</item>
+ <item msgid="3028994095749238254">"Уключана"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index f8793b3..3a28329 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Преглед на всички"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Използване на Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Установена е връзка"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Запазено"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекратяване на връзката"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активиране"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично включване отново утре"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Bluetooth се използва от различни функции, като например „Бързо споделяне“, „Намиране на устройството ми“ и местоположението на устройството"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ще се включи утре в 5:00 ч."</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Записване на проблем"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Стартиране"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Спиране"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Сигнал за грешка"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"С какво имахте проблем?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изберете тип проблем"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис на екрана"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандартен"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Среден"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Висок"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухови апарати"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухови апарати"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Сдвояване на ново устройство"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за сдвояване на ново устройство"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се отблокира ли микрофонът на устройството?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се отблокира ли камерата на устройството?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се отблокират ли камерата и микрофонът на устройството?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Превкл. на клавиат. подредба"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"или"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Изчистване на заявката за търсене"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Клавишни комбинации"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Клавишни комбинации"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Търсете комбинации"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Няма клавишни комбинации"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Система"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Отваряне на Асистент"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Заключване на екрана"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Създаване на бележка"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Едновременно изпълняване на няколко задачи в системата"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Преминаване към разделен екран с текущото приложение отдясно"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Преминаване към разделен екран с текущото приложение отляво"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Изпълняване на няколко задачи едновременно"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Използване на разделен екран с текущото приложение вдясно"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Използване на разделен екран с текущото приложение вляво"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Превключване от разделен към цял екран"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Превключване към приложението вдясно/отдолу в режима на разделен екран"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Превключване към приложението вляво/отгоре в режима на разделен екран"</string>
@@ -1285,7 +1290,7 @@
<string name="dismiss_dialog" msgid="2195508495854675882">"Отхвърляне"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Свързан е екран"</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-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
index 381b0f0..e2fd653 100644
--- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Изключено"</item>
<item msgid="5137565285664080143">"Включено"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Не е налице"</item>
+ <item msgid="3079622119444911877">"Изкл."</item>
+ <item msgid="3028994095749238254">"Вкл."</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index f6de0e3..ec704b8 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"সব দেখুন"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ব্লুটুথ ব্যবহার করুন"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"কানেক্ট করা আছে"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"সেভ করা আছে"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ডিসকানেক্ট করুন"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"চালু করুন"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"আগামীকাল অটোমেটিক আবার চালু হবে"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"\'দ্রুত শেয়ার\', \'Find My Device\' ও \'ডিভাইস লোকেশনের\' মতো ফিচার ব্লুটুথ ব্যবহার করে"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"আগামীকাল ভোর ৫টায় ব্লুটুথ চালু হবে"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string>
@@ -333,7 +341,7 @@
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"সূর্যোদয় পর্যন্ত"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"<xliff:g id="TIME">%s</xliff:g> এ চালু হবে"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> পর্যন্ত"</string>
- <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"গাঢ় থিম"</string>
+ <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"ডার্ক থিম"</string>
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ব্যাটারি সেভার"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"সূর্যাস্তে চালু হবে"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"সূর্যোদয় পর্যন্ত"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"রেকর্ডিংয়ে সমস্যা"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"শুরু করুন"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"বন্ধ করুন"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"সমস্যার রিপোর্ট"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ডিভাইস ব্যবহার করার সময় কোথায় অসুবিধা হয়েছিল?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"সমস্যার প্রকার বেছে নিন"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"স্ক্রিন রেকর্ড"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"স্ট্যান্ডার্ড"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"মিডিয়াম"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"হাই"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"হিয়ারিং ডিভাইস"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"হিয়ারিং ডিভাইস"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"নতুন ডিভাইস পেয়ার করুন"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইস পেয়ার করতে ক্লিক করুন"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইসের মাইক্রোফোন আনব্লক করতে চান?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইসের ক্যামেরা আনব্লক করতে চান?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইসের ক্যামেরা এবং মাইক্রোফোন আনব্লক করতে চান?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"কীবোর্ড লে-আউট পাল্টান"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"অথবা"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"সার্চ কোয়েরি মুছুন"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"শর্টকাট"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"কীবোর্ড শর্টকাট"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"শর্টকাট সার্চ করুন"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"কোনও শর্টকার্ট পাওয়া যায়নি"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"সিস্টেম"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant খুলুন"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"লক স্ক্রিন"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"একটি নোট লিখুন"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"সিস্টেম মাল্টিটাস্কিং"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"ডানদিকে থাকা বর্তমান অ্যাপ ব্যবহার করে \'স্প্লিট স্ক্রিন\' যোগ করুন"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"বাঁদিকে থাকা বর্তমান অ্যাপ ব্যবহার করে \'স্প্লিট স্ক্রিন\' যোগ করুন"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"মাল্টিটাস্কিং"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"ডানদিকে বর্তমান অ্যাপে স্প্লিট স্ক্রিন ব্যবহার করুন"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"বাঁদিকে বর্তমান অ্যাপে স্প্লিট স্ক্রিন ব্যবহার করুন"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"\'স্প্লিট স্ক্রিন\' থেকে ফুল স্ক্রিনে পাল্টান"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"স্প্লিট স্ক্রিন ব্যবহার করার সময় ডানদিকের বা নিচের অ্যাপে পাল্টে নিন"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"স্প্লিট স্ক্রিন ব্যবহার করার সময় বাঁদিকের বা উপরের অ্যাপে পাল্টে নিন"</string>
diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
index 2eebd97..6e4dfbf 100644
--- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"বন্ধ আছে"</item>
<item msgid="5137565285664080143">"চালু আছে"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"উপলভ্য নেই"</item>
+ <item msgid="3079622119444911877">"বন্ধ আছে"</item>
+ <item msgid="3028994095749238254">"চালু আছে"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 9347757..fd77f14 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Prikaži sve"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekid veze"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovo sutra"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcije kao što su Quick Share, Pronađi moj uređaj i lokacija uređaja koriste Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth će se uključiti sutra u 5:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Snimite problem"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Pokrenite"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavite"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvještaj o grešci"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Koji dio uređaja je imao problem?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardno"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Srednje"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Visoko"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni aparati"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da uparite novi uređaj"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokirati mikrofon uređaja?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokirati kameru uređaja?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokirati kameru i mikrofon uređaja?"</string>
@@ -448,8 +453,8 @@
<string name="button_to_remove_widget" msgid="3948204829181214098">"Uklanjanje"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodajte vidžet"</string>
<string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gotovo"</string>
- <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Dodaj widgete"</string>
- <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Brzo pristupajte widgetima omiljenih aplikacija bez otključavanja tableta."</string>
+ <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Dodajte vidžet"</string>
+ <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Dobijte brz pristup omiljenim vidžetima aplikacija bez otključavanja tableta."</string>
<string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Dozvoliti bilo koji vidžet na zaključanom ekranu?"</string>
<string name="button_text_to_open_settings" msgid="1987729256950941628">"Otvori postavke"</string>
<string name="work_mode_off_title" msgid="5794818421357835873">"Pokrenuti poslovne aplikacije?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Zamijeni raspored tastature"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ili"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Brisanje upita za pretraživanje"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Prečice"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Prečice na tastaturi"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pretraživanje prečica"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nisu pronađene prečice"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistem"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvaranje Asistenta"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zaključavanje ekrana"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Pisanje bilješke"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking sistema"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Otvaranje podijeljenog ekrana s trenutnom aplikacijom na desnoj strani"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Otvaranje podijeljenog ekrana s trenutnom aplikacijom na lijevoj strani"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Korištenje podijeljenog ekrana s trenutnom aplikacijom na desnoj strani"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Korištenje podijeljenog ekrana s trenutnom aplikacijom na lijevoj strani"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Prebacivanje s podijeljenog ekrana na prikaz preko cijelog ekrana"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pređite u aplikaciju desno ili ispod dok koristite podijeljeni ekran"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju lijevo ili iznad dok koristite podijeljeni ekran"</string>
diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
index e09cab5..df0b786 100644
--- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Isključeno"</item>
<item msgid="5137565285664080143">"Uključeno"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Nedostupno"</item>
+ <item msgid="3079622119444911877">"Isključeno"</item>
+ <item msgid="3028994095749238254">"Uključeno"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 40532ca..aeb0ef0 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Mostra-ho tot"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Utilitza\'l"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connectat"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Desat"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconnecta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activa"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Torna\'l a activar automàticament demà"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funcions com ara Quick Share, Troba el meu dispositiu i la ubicació del dispositiu utilitzen el Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"El Bluetooth s\'activarà demà a les 5:00 h"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Àudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculars"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Registra el problema"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Inicia"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Atura"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe d\'errors"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"L\'experiència amb el dispositiu s\'ha vist afectada?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipus de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravació de pantalla"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Estàndard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Mitjà"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Alt"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audiòfons"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Audiòfons"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincula un dispositiu nou"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fes clic per vincular un dispositiu nou"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vols desbloquejar el micròfon del dispositiu?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vols desbloquejar la càmera del dispositiu?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vols desbloquejar la càmera i el micròfon del dispositiu?"</string>
@@ -606,7 +611,7 @@
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toca per silenciar."</string>
<string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Control de soroll"</string>
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Àudio espacial"</string>
- <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Desactiva"</string>
+ <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Desactivat"</string>
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fix"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguiment del cap"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toca per canviar el mode de timbre"</string>
@@ -745,12 +750,12 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Canvia disposició de teclat"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"o"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Esborra la consulta de cerca"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Dreceres"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Tecles de drecera"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Cerca dreceres"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"No s\'ha trobat cap drecera"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Entrada"</string>
- <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Obre aplicacions"</string>
+ <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Aplicacions obertes"</string>
<string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Aplicació actual"</string>
<string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"S\'estan mostrant els resultats de la cerca"</string>
<string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"S\'estan mostrant les dreceres del sistema"</string>
@@ -768,11 +773,11 @@
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Obre la llista d\'aplicacions"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Obre la configuració"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Obre l\'Assistent"</string>
- <string name="group_system_lock_screen" msgid="7391191300363416543">"Pantalla de bloqueig"</string>
+ <string name="group_system_lock_screen" msgid="7391191300363416543">"Bloqueja la pantalla"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Crea una nota"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasques del sistema"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Entra al mode de pantalla dividida amb l\'aplicació actual a la dreta"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Entra al mode de pantalla dividida amb l\'aplicació actual a l\'esquerra"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasca"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Utilitza la pantalla dividida amb l\'aplicació actual a la dreta"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Utilitza la pantalla dividida amb l\'aplicació actual a l\'esquerra"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Canvia de pantalla dividida a pantalla completa"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Canvia a l\'aplicació de la dreta o de sota amb la pantalla dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Canvia a l\'aplicació de l\'esquerra o de dalt amb la pantalla dividida"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroil·luminació del teclat"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivell %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controls de la llar"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Utilitza controls de la llar com a salvapantalles"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Utilitza controls de la llar com a estalvi de pantalla"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
index 6a36b6f..67eb853 100644
--- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Desactivat"</item>
<item msgid="5137565285664080143">"Activat"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"No disponible"</item>
+ <item msgid="3079622119444911877">"Desactivat"</item>
+ <item msgid="3028994095749238254">"Activat"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 16c2914..891f836 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Zobrazit vše"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Použít Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Připojeno"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uloženo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojit"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovat"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Zítra znovu automaticky zapnout"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkce jako Quick Share, Najdi moje zařízení a vyhledávání zařízení používají Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth se zapne zítra v 5:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Sluchátka"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Zaznamenat problém"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Spustit"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Ukončit"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Zpráva o chybě"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Co v zařízení bylo ovlivněno?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte druh problém"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Záznam obrazovky"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardní"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Střední"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Vysoká"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Naslouchátka"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Naslouchátka"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovat nové zařízení"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zařízení"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Odblokovat mikrofon zařízení?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Odblokovat fotoaparát zařízení?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Odblokovat fotoaparát a mikrofon zařízení?"</string>
@@ -680,7 +685,7 @@
<string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"Zobrazuje se v horní části sekce konverzací a na obrazovce uzamčení se objevuje jako profilová fotka, má podobu bubliny"</string>
<string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"Zobrazuje se v horní části sekce konverzací a na obrazovce uzamčení se objevuje jako profilová fotka, deaktivuje režim Nerušit"</string>
<string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Zobrazuje se v horní části sekce konverzací a na obrazovce uzamčení se objevuje jako profilová fotka, má podobu bubliny a deaktivuje režim Nerušit"</string>
- <string name="notification_priority_title" msgid="2079708866333537093">"Priorita"</string>
+ <string name="notification_priority_title" msgid="2079708866333537093">"Prioritní"</string>
<string name="no_shortcut" msgid="8257177117568230126">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> funkce konverzace nepodporuje"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"Tato oznámení nelze upravit."</string>
<string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Upozornění na hovor nelze upravit."</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Přepnout rozložení klávesnice"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"nebo"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Vymazat vyhledávaný dotaz"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Zkratky"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Klávesové zkratky"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Vyhledat zkratky"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Žádné zkratky nenalezeny"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Systém"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otevřít Asistenta"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Uzamknout obrazovku"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Vytvořit poznámku"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Systémový multitasking"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Přepnout na rozdělenou obrazovku s aktuálními aplikacemi napravo"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Přepnout na rozdělenou obrazovku s aktuálními aplikacemi nalevo"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Použít rozdělenou obrazovku se stávající aplikací vpravo"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Použít rozdělenou obrazovku se stávající aplikací vlevo"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Přepnout z rozdělené obrazovky na celou obrazovku"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Přechod na aplikaci vpravo nebo dole v režimu rozdělené obrazovky"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Přechod na aplikaci vlevo nebo nahoře v režimu rozdělené obrazovky"</string>
@@ -1147,7 +1152,7 @@
<string name="audio_status" msgid="4237055636967709208">"Poslouchá"</string>
<string name="game_status" msgid="1340694320630973259">"Hraji hru"</string>
<string name="empty_user_name" msgid="3389155775773578300">"Přátelé"</string>
- <string name="empty_status" msgid="5938893404951307749">"Proberem to večer?"</string>
+ <string name="empty_status" msgid="5938893404951307749">"Probereme to večer?"</string>
<string name="status_before_loading" msgid="1500477307859631381">"Obsah se brzy zobrazí"</string>
<string name="missed_call" msgid="4228016077700161689">"Zmeškaný hovor"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
@@ -1269,7 +1274,7 @@
<string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"Nainstalovat pracovní telefonní aplikaci"</string>
<string name="call_from_work_profile_close" msgid="5830072964434474143">"Zrušit"</string>
<string name="lock_screen_settings" msgid="6152703934761402399">"Přizpůsobit obrazovku uzamčení"</string>
- <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Pokud chcete upravit obrazovku uzamčení, odemkněte zařízení"</string>
+ <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Obrazovku uzamčení upravíte, když zařízení odemknete"</string>
<string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Síť Wi-Fi není dostupná"</string>
<string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera je blokována"</string>
<string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera a mikrofon jsou blokovány"</string>
diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
index 02a0f5f..ae533a8 100644
--- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Vypnuto"</item>
<item msgid="5137565285664080143">"Zapnuto"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Není k dispozici"</item>
+ <item msgid="3079622119444911877">"Vypnuto"</item>
+ <item msgid="3028994095749238254">"Zapnuto"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 4754dbb..5c721a1 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Se alt"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Brug Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Der er oprettet forbindelse"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gemt"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"afbryd forbindelse"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivér"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivér automatisk igen i morgen"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funktioner som f.eks. Quick Share, Find min enhed og enhedslokation anvender Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth aktiveres i morgen kl. 5.00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Optag problem"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Fejlrapport"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del af din enhedsoplevelse blev påvirket?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vælg problemtype"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skærmoptagelse"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Middel"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Høj"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Høreapparater"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Par ny enhed"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik for at parre en ny enhed"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du fjerne blokeringen af enhedens mikrofon?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du fjerne blokeringen af enhedens kamera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du fjerne blokeringen af enhedens kamera og mikrofon?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Skift tastaturlayout"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"eller"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Ryd søgeforespørgsel"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Genveje"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Tastaturgenveje"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Søg efter genveje"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ingen genveje blev fundet"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"System"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Åbn Assistent"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lås skærm"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Skriv en note"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Systemmultitasking"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Start opdelt skærm med aktuel app til højre"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Start opdelt skærm med aktuel app til venstre"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Brug opdelt skærm med aktuel app til højre"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Brug opdelt skærm med aktuel app til venstre"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Skift fra opdelt skærm til fuld skærm"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skift til en app til højre eller nedenfor, når du bruger opdelt skærm"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skift til en app til venstre eller ovenfor, når du bruger opdelt skærm"</string>
diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml
index 598fcfe..2c3b053 100644
--- a/packages/SystemUI/res/values-da/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Fra"</item>
<item msgid="5137565285664080143">"Til"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Ikke tilgængelig"</item>
+ <item msgid="3079622119444911877">"Fra"</item>
+ <item msgid="3028994095749238254">"Til"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 2fd26bc..510dba9 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -198,7 +198,7 @@
<string name="face_re_enroll_notification_title" msgid="1850838867718410520">"Entsperrung per Gesichtserkennung neu einrichten"</string>
<string name="face_re_enroll_notification_name" msgid="7384545252206120659">"Entsperrung per Gesichtserkennung"</string>
<string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Entsperrung per Gesichtserkennung einrichten"</string>
- <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Wenn du die Entsperrung per Gesichtserkennung neu einrichtest, wird dein aktuelles Gesichtsmodell gelöscht.\n\nDu musst diese Funktion neu einrichten, damit du dein Smartphone weiterhin mit deinem Gesicht entsperren kannst."</string>
+ <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Wenn du die Entsperrung per Gesichtserkennung neu einrichtest, wird dein aktuelles Gesichtsmodell gelöscht.\n\nDu musst diese Funktion neu einrichten, damit du dein Smartphone weiterhin mithilfe der Gesichtserkennung entsperren kannst."</string>
<string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Die Entsperrung per Gesichtserkennung konnte nicht eingerichtet werden. Gehe zu den Einstellungen und versuche es noch einmal."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Berühre den Fingerabdrucksensor"</string>
<string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"Tippe zum Fortfahren auf das Symbol „Entsperren“"</string>
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alle anzeigen"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth verwenden"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbunden"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gespeichert"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Verknüpfung aufheben"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivieren"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen automatisch wieder aktivieren"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Für Funktionen wie Quick Share, „Mein Gerät finden“ und den Gerätestandort wird Bluetooth verwendet"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth wird morgen um 5:00 Uhr aktiviert"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Problem aufnehmen"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Aufnahme starten"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Aufnahme beenden"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Fehlerbericht"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Welche Bereiche des Geräts waren betroffen?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Art des Problems auswählen"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Bildschirmaufnahme"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Mittel"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Hoch"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörgeräte"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hörgeräte"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Neues Gerät koppeln"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicken, um neues Gerät zu koppeln"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Blockierung des Gerätemikrofons aufheben?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Blockierung der Gerätekamera aufheben?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blockierung von Gerätekamera und Gerätemikrofon aufheben?"</string>
@@ -448,10 +453,8 @@
<string name="button_to_remove_widget" msgid="3948204829181214098">"Entfernen"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget hinzufügen"</string>
<string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Fertig"</string>
- <!-- no translation found for label_for_button_in_empty_state_cta (7314975555382055823) -->
- <skip />
- <!-- no translation found for title_for_empty_state_cta (6161654421223450530) -->
- <skip />
+ <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Widgets hinzufügen"</string>
+ <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Greife schnell auf deine App-Widgets zu, ohne das Tablet entsperren zu müssen."</string>
<string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Beliebige Widgets auf Sperrbildschirm zulassen?"</string>
<string name="button_text_to_open_settings" msgid="1987729256950941628">"Einstellungen öffnen"</string>
<string name="work_mode_off_title" msgid="5794818421357835873">"Geschäftliche Apps nicht mehr pausieren?"</string>
@@ -747,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Tastaturlayout wechseln"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"oder"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Suchanfrage löschen"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Tastenkombinationen"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Tastenkombinationen"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Tastenkombinationen suchen"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Keine gefunden"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"System"</string>
@@ -772,16 +775,16 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant öffnen"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Sperrbildschirm"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Notiz machen"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"System-Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Splitscreen aktivieren, aktuelle App rechts"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Splitscreen aktivieren, aktuelle App links"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Splitscreen mit der aktuellen App auf der rechten Seite nutzen"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Splitscreen mit der aktuellen App auf der linken Seite nutzen"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Vom Splitscreen zum Vollbild wechseln"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Im Splitscreen-Modus rechts oder unten zu einer App wechseln"</string>
- <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Im Splitscreen-Modus links oder oben zu einer App wechseln"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Im Splitscreen-Modus zu einer App rechts oder unten wechseln"</string>
+ <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Im Splitscreen-Modus zu einer App links oder oben wechseln"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Im Splitscreen: eine App durch eine andere ersetzen"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Eingabe"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Zur nächsten Sprache wechseln"</string>
- <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Zu vorheriger Sprache wechseln"</string>
+ <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Zur vorherigen Sprache wechseln"</string>
<string name="input_access_emoji" msgid="8105642858900406351">"Auf Emoji zugreifen"</string>
<string name="input_access_voice_typing" msgid="7291201476395326141">"Auf Spracheingabe zugreifen"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Apps"</string>
@@ -793,7 +796,7 @@
<string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"Musik"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalender"</string>
<string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Rechner"</string>
- <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Karten"</string>
+ <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
<string name="volume_and_do_not_disturb" msgid="502044092739382832">"Bitte nicht stören"</string>
<string name="volume_dnd_silent" msgid="4154597281458298093">"Tastenkombination für Lautstärketasten"</string>
<string name="battery" msgid="769686279459897127">"Akku"</string>
diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml
index 4b4eed5..0606cc7 100644
--- a/packages/SystemUI/res/values-de/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Aus"</item>
<item msgid="5137565285664080143">"An"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Nicht verfügbar"</item>
+ <item msgid="3079622119444911877">"Aus"</item>
+ <item msgid="3028994095749238254">"An"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 0c3f0d5..a881536 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Εμφάνιση όλων"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Χρήση Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Συνδέθηκε"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Αποθηκεύτηκε"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"αποσύνδεση"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ενεργοποίηση"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Αυτόματη ενεργοποίηση ξανά αύριο"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Λειτουργίες όπως το Quick Share, η Εύρεση συσκευής και η τοποθεσία της συσκευής χρησιμοποιούν Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Το Bluetooth θα ενεργοποιηθεί αύριο στις 5 π.μ."</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Εγγραφή προβλήματος"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Έναρξη"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Διακοπή"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Αναφορά σφάλματος"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ποιο κομμάτι της εμπειρίας συσκευής επηρεάστηκε;"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Επιλογή τύπου προβλήματος"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Εγγραφή οθόνης"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Τυπική"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Μέτρια"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Υψηλή"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Συσκευές ακοής"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Συσκευές ακοής"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Σύζευξη νέας συσκευής"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Κάντε κλικ για σύζευξη νέας συσκευής"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Κατάργηση αποκλεισμού μικροφώνου συσκευής;"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Κατάργηση αποκλεισμού κάμερας συσκευής;"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Κατάργηση αποκλεισμού κάμερας και μικροφώνου συσκευής;"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Αλλαγή διάταξης πληκτρολογίου"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ή"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Διαγραφή ερωτήματος αναζήτησης"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Συντομεύσεις"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Συντομεύσ. πληκτρολογίου"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Αναζήτηση συντομεύσεων"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Δεν βρέθηκαν συντομεύσεις"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Σύστημα"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Άνοιγμα Βοηθού"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Κλείδωμα οθόνης"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Δημιουργία σημείωσης"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Πολυδιεργασία συστήματος"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Ενεργοποίηση διαχωρισμού οθόνης με την τρέχουσα εφαρμογή στα δεξιά"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Ενεργοποίηση διαχωρισμού οθόνης με την τρέχουσα εφαρμογή στα αριστερά"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Πολυδιεργασία"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Χρήση διαχωρισμού οθόνης με την τρέχουσα εφαρμογή στα δεξιά"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Χρήση διαχωρισμού οθόνης με την τρέχουσα εφαρμογή στα αριστερά"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Εναλλαγή από διαχωρισμό οθόνης σε πλήρη οθόνη"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Εναλλαγή στην εφαρμογή δεξιά ή κάτω κατά τη χρήση διαχωρισμού οθόνης"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Εναλλαγή σε εφαρμογή αριστερά ή επάνω κατά τη χρήση διαχωρισμού οθόνης"</string>
diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml
index e4c6854..d4545ff 100644
--- a/packages/SystemUI/res/values-el/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Ανενεργό"</item>
<item msgid="5137565285664080143">"Ενεργό"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Μη διαθέσιμη"</item>
+ <item msgid="3079622119444911877">"Ανενεργή"</item>
+ <item msgid="3028994095749238254">"Ενεργή"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index a8848a5..d2af8a6 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device and device location use Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5.00 a.m."</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bug report"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Medium"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"High"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -551,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"This device is managed by your parent. Your parent can see and manage information such as the apps that you use, your location and your screen time."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by trust agent"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Device was locked, too many authentication attempts"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Device was locked – too many authentication attempts"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Device locked\nFailed authentication"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Switch keyboard layout"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"or"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Clear search query"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Shortcuts"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Keyboard shortcuts"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Search shortcuts"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"No shortcuts found"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"System"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Open Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"System multitasking"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Enter split screen with current app to RHS"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Enter split screen with current app to LHS"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multi-tasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
index 304abe1..39dd7c8 100644
--- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Off"</item>
<item msgid="5137565285664080143">"On"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Unavailable"</item>
+ <item msgid="3079622119444911877">"Off"</item>
+ <item msgid="3028994095749238254">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 4b69255..836eefa 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -270,12 +270,15 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio Sharing"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device, and device location use Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5 AM"</string>
+ <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
+ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio Sharing"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +353,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Record Issue"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bug Report"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
@@ -741,7 +745,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Switch keyboard layout"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"or"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Clear search query"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Shortcuts"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Keyboard Shortcuts"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Search shortcuts"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"No shortcuts found"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"System"</string>
@@ -766,9 +770,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Open assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"System multitasking"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Enter split screen with current app to RHS"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Enter split screen with current app to LHS"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to app on right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to app on left or above while using split screen"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index a8848a5..d2af8a6 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device and device location use Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5.00 a.m."</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bug report"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Medium"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"High"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -551,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"This device is managed by your parent. Your parent can see and manage information such as the apps that you use, your location and your screen time."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by trust agent"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Device was locked, too many authentication attempts"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Device was locked – too many authentication attempts"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Device locked\nFailed authentication"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Switch keyboard layout"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"or"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Clear search query"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Shortcuts"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Keyboard shortcuts"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Search shortcuts"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"No shortcuts found"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"System"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Open Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"System multitasking"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Enter split screen with current app to RHS"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Enter split screen with current app to LHS"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multi-tasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
index 304abe1..39dd7c8 100644
--- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Off"</item>
<item msgid="5137565285664080143">"On"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Unavailable"</item>
+ <item msgid="3079622119444911877">"Off"</item>
+ <item msgid="3028994095749238254">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index a8848a5..d2af8a6 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device and device location use Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5.00 a.m."</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bug report"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Medium"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"High"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -551,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"This device is managed by your parent. Your parent can see and manage information such as the apps that you use, your location and your screen time."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by trust agent"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Device was locked, too many authentication attempts"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Device was locked – too many authentication attempts"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Device locked\nFailed authentication"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Switch keyboard layout"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"or"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Clear search query"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Shortcuts"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Keyboard shortcuts"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Search shortcuts"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"No shortcuts found"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"System"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Open Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"System multitasking"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Enter split screen with current app to RHS"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Enter split screen with current app to LHS"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multi-tasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
index 304abe1..39dd7c8 100644
--- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Off"</item>
<item msgid="5137565285664080143">"On"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Unavailable"</item>
+ <item msgid="3079622119444911877">"Off"</item>
+ <item msgid="3028994095749238254">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 3ed9fc5..77ef52e 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -270,12 +270,15 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio Sharing"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device, and device location use Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5 AM"</string>
+ <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
+ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio Sharing"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +353,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Record Issue"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bug Report"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
@@ -741,7 +745,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Switch keyboard layout"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"or"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Clear search query"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Shortcuts"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Keyboard Shortcuts"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Search shortcuts"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"No shortcuts found"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"System"</string>
@@ -766,9 +770,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Open assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"System multitasking"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Enter split screen with current app to RHS"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Enter split screen with current app to LHS"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use split screen with current app on the right"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use split screen with current app on the left"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Switch from split screen to full screen"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to app on right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to app on left or above while using split screen"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 3755fb6..c33213a 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver todos"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver a activar automáticamente mañana"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Las funciones como Quick Share, Encontrar mi dispositivo y la ubicación del dispositivo usan Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Se activará el Bluetooth mañana a las 5 a.m."</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Grabar error"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe de errores"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu exp. del disp. se vio afectada?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleccionar tipo de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabadora de pant."</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Estándar"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Medio"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Alto"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo nuevo"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para vincular un dispositivo nuevo"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Quieres desbloquear el micrófono del dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Quieres desbloquear la cámara del dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Quieres desbloquear la cámara y el micrófono del dispositivo?"</string>
@@ -668,7 +673,7 @@
<string name="notification_alert_title" msgid="3656229781017543655">"Predeterminada"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"No suena ni vibra, y aparece en la parte inferior de la sección de conversaciones."</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"No suena ni vibra, y aparece en la parte inferior de la sección de conversaciones"</string>
<string name="notification_channel_summary_default" msgid="777294388712200605">"Puede sonar o vibrar según la configuración del dispositivo"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Puede sonar o vibrar según la configuración del dispositivo. Conversaciones de la burbuja de <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Dejar que el sistema determine si esta notificación debe emitir un sonido o una vibración"</string>
@@ -715,7 +720,7 @@
<string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Flecha hacia arriba"</string>
<string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Flecha hacia abajo"</string>
<string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flecha a la izquierda"</string>
- <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flecha hacia la derecha"</string>
+ <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flecha a la derecha"</string>
<string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centro"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Espacio"</string>
@@ -743,10 +748,10 @@
<string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notificaciones"</string>
<string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Ver combinaciones de teclas"</string>
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Cambiar diseño del teclado"</string>
- <string name="keyboard_shortcut_join" msgid="3578314570034512676">"o bien"</string>
+ <string name="keyboard_shortcut_join" msgid="3578314570034512676">"o"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Borrar búsqueda"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Combinaciones de teclas"</string>
- <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Buscar comb. de teclas"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Combinaciones de teclas"</string>
+ <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Buscar combinaciones de teclas"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"No hay comb. de teclas"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Entrada"</string>
@@ -770,12 +775,12 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir Asistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Bloquear la pantalla"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Crear una nota"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Tareas múltiples del sistema"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Activar pantalla dividida con la app actual en el lado derecho (RHS)"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Activar pantalla dividida con la app actual en el lado izquierdo (LHS)"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Tareas múltiples"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar la pantalla dividida con la app actual a la derecha"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar la pantalla dividida con la app actual a la izquierda"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Cambiar de pantalla dividida a pantalla completa"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Ubica la app a la derecha o abajo cuando usas la pantalla dividida"</string>
- <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ubica la app a la izquierda o arriba cuando usas la pantalla dividida"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Ubicar la app a la derecha o abajo cuando usas la pantalla dividida"</string>
+ <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ubicar la app a la izquierda o arriba cuando usas la pantalla dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Durante pantalla dividida: Reemplaza una app con otra"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Cambiar al próximo idioma"</string>
@@ -1194,7 +1199,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está activa}many{# apps están activas}other{# apps están activas}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nueva información"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps activas"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Estas apps están activas y en ejecución, incluso mientras no las usas. Esto mejora su funcionalidad, pero también afecta la duración de batería."</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Estas apps están activas y en ejecución, incluso mientras no las usas. Esto mejora su funcionalidad, pero también afecta la duración de la batería."</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Detener"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Detenida"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Listo"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Accede a controles de la casa como prot. de pant."</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Accede rápidamente a controles de la casa como prot. de pantalla"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
index 71efef9..869efff 100644
--- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Desactivado"</item>
<item msgid="5137565285664080143">"Activado"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"No disponibles"</item>
+ <item msgid="3079622119444911877">"Desactivados"</item>
+ <item msgid="3028994095749238254">"Activados"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 01afa29..1dc93fde 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver todos"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver a activar automáticamente mañana"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Las funciones como Quick Share y Encontrar mi dispositivo, y la ubicación del dispositivo usan Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"El Bluetooth se activará mañana a las 5:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Problema de grabación"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe errores"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu experiencia se ha visto afectada?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipo de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabar pantalla"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Estándar"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Medio"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Alto"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audífonos"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Audífonos"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Emparejar nuevo dispositivo"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para emparejar un nuevo dispositivo"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Desbloquear el micrófono del dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Desbloquear la cámara del dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Desbloquear la cámara y el micrófono del dispositivo?"</string>
@@ -481,7 +486,7 @@
<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>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha inhabilitado esta opción"</string>
- <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"¿Empezar a enviar contenido?"</string>
+ <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"¿Empezar a enviar?"</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>
<string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Empezar a enviar"</string>
@@ -714,8 +719,8 @@
<string name="keyboard_key_back" msgid="4185420465469481999">"Atrás"</string>
<string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Flecha hacia arriba"</string>
<string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Flecha hacia abajo"</string>
- <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flecha hacia la izquierda"</string>
- <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flecha hacia la derecha"</string>
+ <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flecha izquierda"</string>
+ <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flecha derecha"</string>
<string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centro"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tabulador"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Espacio"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Cambiar diseño del teclado"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"o"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Borrar la consulta de búsqueda"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Combinaciones de teclas"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Combinaciones de teclas"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Buscar combinaciones de teclas"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ninguna encontrada"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string>
@@ -769,10 +774,10 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Abrir ajustes"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir el Asistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Pantalla de bloqueo"</string>
- <string name="group_system_quick_memo" msgid="3764560265935722903">"Escribe una nota"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Función multitarea del sistema"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Iniciar pantalla dividida con esta aplicación en el lado derecho"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Iniciar pantalla dividida con esta aplicación en el lado izquierdo"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Escribir una nota"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarea"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar la pantalla dividida con la aplicación actual a la derecha"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar la pantalla dividida con la aplicación actual a la izquierda"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Cambiar de pantalla dividida a pantalla completa"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Cambiar a la app de la derecha o de abajo en pantalla dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar a la app de la izquierda o de arriba en pantalla dividida"</string>
diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml
index bd90056..5dbb2c1 100644
--- a/packages/SystemUI/res/values-es/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Desactivado"</item>
<item msgid="5137565285664080143">"Activado"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"No disponibles"</item>
+ <item msgid="3079622119444911877">"Desactivados"</item>
+ <item msgid="3028994095749238254">"Activados"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 52cbb0f..fea407d 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Kuva kõik"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Kasuta Bluetoothi"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ühendatud"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvestatud"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkesta ühendus"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveeri"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Lülita automaatselt homme uuesti sisse"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funktsioonid, nagu Kiirjagamine, Leia mu seade ja seadme asukoht, kasutavad Bluetoothi"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth lülitatakse sisse homme kell viis hommikul"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> akut"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Probleemi salvestamine"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Alusta"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Peata"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Veaaruanne"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Millist seadme kasutuskogemuse osa see mõjutas?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valige probleemi tüüp"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekraanisalvestus"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Tavaline"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Keskmine"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Kõrge"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuuldeseadmed"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Kuuldeseadmed"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uue seadme sidumine"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Uue seadme sidumiseks klõpsake"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kas tühistada seadme mikrofoni blokeerimine?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kas tühistada seadme kaamera blokeerimine?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kas tühistada seadme kaamera ja mikrofoni blokeerimine?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Klaviatuuripaigutuse vahetus"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"või"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Otsingupäringu tühjendamine"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Otseteed"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Klaviatuuri otseteed"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Otseteede otsing"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Otseteid ei leitud"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Süsteem"</string>
@@ -769,10 +774,10 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Seadete avamine"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistendi avamine"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lukustuskuva"</string>
- <string name="group_system_quick_memo" msgid="3764560265935722903">"Kirjuta märkus"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Süsteemi multitegumtöö"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Ekraanikuva jagamine, nii et praegune rakendus on paremal"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Ekraanikuva jagamine, nii et praegune rakendus on vasakul"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Märkme tegemine"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitegumtöö"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Jagatud ekraanikuva kasutamine, praegune rakendus kuvatakse paremal"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Jagatud ekraanikuva kasutamine, praegune rakendus kuvatakse vasakul"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Jagatud ekraanikuvalt täisekraanile lülitamine"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Paremale või alumisele rakendusele lülitamine jagatud ekraani ajal"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Vasakule või ülemisele rakendusele lülitamine jagatud ekraani ajal"</string>
diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml
index 55bff30..704649e 100644
--- a/packages/SystemUI/res/values-et/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Väljas"</item>
<item msgid="5137565285664080143">"Sees"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Pole saadaval"</item>
+ <item msgid="3079622119444911877">"Välja lülitatud"</item>
+ <item msgid="3028994095749238254">"Sisse lülitatud"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index b0056f6..41de1d4 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ikusi guztiak"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Erabili Bluetootha"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Konektatuta"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gordeta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deskonektatu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktibatu"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktibatu automatikoki berriro bihar"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, Bilatu nire gailua, gailuaren kokapena eta beste eginbide batzuek Bluetootha darabilte"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetootha bihar 05:00etan aktibatuko da"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string>
@@ -329,13 +337,13 @@
<string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Laneko aplikazioak"</string>
<string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pausatuta"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Gaueko argia"</string>
- <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ilunabarrean"</string>
+ <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ilunabarrean aktibatuta"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Ilunabarrera arte"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"Aktibatze-ordua: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> arte"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Gai iluna"</string>
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Bateria-aurreztailea"</string>
- <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Ilunabarrean aktibatuko da"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Ilunabarrean aktibatuta"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Egunsentira arte"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktibatze-ordua: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Desaktibatze-ordua: <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Arazo bat dago grabaketarekin"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Hasi"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Gelditu"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Akatsen txostena"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Gailuaren erabileraren zer alderdiri eragin dio?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Hautatu arazo mota"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pantaila-grabaketa"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Arrunta"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Tartekoa"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Altua"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Entzumen-gailuak"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Entzumen-gailuak"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parekatu beste gailu bat"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Egin klik beste gailu bat parekatzeko"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Gailuaren mikrofonoa desblokeatu nahi duzu?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Gailuaren kamera desblokeatu nahi duzu?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Gailuaren kamera eta mikrofonoa desblokeatu nahi dituzu?"</string>
@@ -737,7 +742,7 @@
<string name="keyboard_key_numpad_template" msgid="7316338238459991821">"Zenbaki-teklatuko <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="notif_inline_reply_remove_attachment_description" msgid="7954075334095405429">"Kendu eranskina"</string>
<string name="keyboard_shortcut_group_system" msgid="1583416273777875970">"Sistema"</string>
- <string name="keyboard_shortcut_group_system_home" msgid="7465138628692109907">"Hasierako pantaila"</string>
+ <string name="keyboard_shortcut_group_system_home" msgid="7465138628692109907">"Orri nagusia"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="8628108256824616927">"Azkenaldikoak"</string>
<string name="keyboard_shortcut_group_system_back" msgid="1055709713218453863">"Atzera"</string>
<string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Jakinarazpenak"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Aldatu tekl. diseinua"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"edo"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Garbitu bilaketa-kontsulta"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Lasterbideak"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Lasterbideak"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Bilatu lasterbideak"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ez da aurkitu lasterbiderik"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Ireki Laguntzailea"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Blokeatu pantaila"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Egin ohar bat"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Zereginen aldibereko sistemaren exekuzioa"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Sartu pantaila zatituaren eskuineko aldean oraingo aplikazioarekin"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Sartu pantaila zatituaren ezkerreko aldean oraingo aplikazioarekin"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Zeregin bat baino gehiago aldi berean exekutatzea"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Erabili pantaila zatitua eta ezarri aplikazio hau eskuinean"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Erabili pantaila zatitua eta ezarri aplikazio hau ezkerrean"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Aldatu pantaila zatitutik pantaila osora"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Aldatu eskuineko edo beheko aplikaziora pantaila zatitua erabiltzean"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Aldatu ezkerreko edo goiko aplikaziora pantaila zatitua erabiltzean"</string>
@@ -790,8 +795,8 @@
<string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMSak"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"Musika"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
- <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Ireki Kalkulagailua"</string>
- <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Ireki Maps"</string>
+ <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Kalkulagailua"</string>
+ <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
<string name="volume_and_do_not_disturb" msgid="502044092739382832">"Ez molestatzeko modua"</string>
<string name="volume_dnd_silent" msgid="4154597281458298093">"Bolumen-botoietarako lasterbidea"</string>
<string name="battery" msgid="769686279459897127">"Bateria"</string>
@@ -1125,7 +1130,7 @@
<string name="basic_status" msgid="2315371112182658176">"Elkarrizketa irekia"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"Elkarrizketa-widgetak"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"Sakatu elkarrizketa bat orri nagusian gehitzeko"</string>
- <string name="no_conversations_text" msgid="5354115541282395015">"Azken elkarrizketak agertuko dira hemen"</string>
+ <string name="no_conversations_text" msgid="5354115541282395015">"Azkenaldiko elkarrizketak agertuko dira hemen"</string>
<string name="priority_conversations" msgid="3967482288896653039">"Lehentasunezko elkarrizketak"</string>
<string name="recent_conversations" msgid="8531874684782574622">"Azken elkarrizketak"</string>
<string name="days_timestamp" msgid="5821854736213214331">"Duela <xliff:g id="DURATION">%1$s</xliff:g> egun"</string>
@@ -1151,7 +1156,7 @@
<string name="status_before_loading" msgid="1500477307859631381">"Laster agertuko da edukia"</string>
<string name="missed_call" msgid="4228016077700161689">"Dei galdua"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
- <string name="people_tile_description" msgid="8154966188085545556">"Ikusi azken mezuak, dei galduak eta egoerari buruzko informazio eguneratua"</string>
+ <string name="people_tile_description" msgid="8154966188085545556">"Ikusi azken mezuak, dei galduak eta egoerei buruzko informazio eguneratua"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Elkarrizketa"</string>
<string name="paused_by_dnd" msgid="7856941866433556428">"Ez molestatzeko moduak pausatu du"</string>
<string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzaileak mezu bat bidali du: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
@@ -1304,6 +1309,6 @@
<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>) aplikazioak erabili du duela gutxi"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Teklatuaren hondoko argia"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d/%2$d maila"</string>
- <string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuak kontrolatzeko aukerak"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Atzitu etxeko gailuak kontrolatzeko aukerak pantaila-babesletik"</string>
+ <string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuen kontrola"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Kontrolatu etxeko gailuak pantaila-babesletik"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
index 7f38d44..13e14e0 100644
--- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Desaktibatuta"</item>
<item msgid="5137565285664080143">"Aktibatuta"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Ez daude erabilgarri"</item>
+ <item msgid="3079622119444911877">"Desaktibatuta"</item>
+ <item msgid="3028994095749238254">"Aktibatuta"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index de97db35..8e72450 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"دیدن همه"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"استفاده از بلوتوث"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"متصل"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ذخیرهشده"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"قطع اتصال"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کردن"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"فردا دوباره بهطور خودکار روشن شود"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ویژگیهایی مثل «همرسانی سریع»، «پیدا کردن دستگاهم»، و مکان دستگاه از بلوتوث استفاده میکنند"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"بلوتوث فردا ۵ ق.ظ روشن خواهد شد"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"شارژ باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"ضبط مشکل"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"شروع"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"توقف"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"گزارش اشکال"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"کدام بخش تجربه استفاده از دستگاه تحتتأثیر قرار گرفت؟"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"انتخاب نوع مشکل"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ضبط صفحهنمایش"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"استاندارد"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"متوسط"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"بالا"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سمعک"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سمعک"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"جفت کردن دستگاه جدید"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"برای جفت کردن دستگاه جدید، کلیک کنید"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"میکروفون دستگاه لغو انسداد شود؟"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"دوربین دستگاه لغو انسداد شود؟"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"دوربین و میکروفون دستگاه لغو انسداد شود؟"</string>
@@ -551,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"این دستگاه را ولیتان مدیریت میکند. ولیتان میتواند اطلاعاتی مثل برنامههایی که استفاده میکنید، مکانتان، و مدت تماشای صفحهتان را ببیند و مدیریت کند."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"با TrustAgent قفل را باز نگهدارید"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"دستگاه قفل شد، تعداد تلاشها برای اصالتسنجی بسیار زیاد بود"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"بهدلیل تلاشهای بیشاز حد برای اصالتسنجی، دستگاه قفل شد"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"دستگاه قفل شد\nاصالتسنجی ناموفق بود"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"تنظیمات صدا"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"تغییر جانمایی صفحهکلید"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"یا"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"پاک کردن پُرسمان جستجو"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"میانبرها"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"میانبرهای صفحهکلید"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"جستجوی میانبرها"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"میانبری پیدا نشد"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"سیستم"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"باز کردن «دستیار»"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"قفل صفحه"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"یادداشتبرداری"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"چندوظیفگی سیستم"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"وارد شدن به صفحهٔ دونیمه با برنامه فعلی در سمت راست"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"وارد شدن به صفحهٔ دونیمه با برنامه فعلی در سمت چپ"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"چندوظیفگی"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"استفاده از صفحهٔ دونیمه با برنامه فعلی در سمت راست"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"استفاده از صفحهٔ دونیمه با برنامه فعلی در سمت چپ"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"جابهجایی از صفحهٔ دونیمه به تمام صفحه"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"رفتن به برنامه سمت راست یا پایین درحین استفاده از صفحهٔ دونیمه"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"رفتن به برنامه سمت چپ یا بالا درحین استفاده از صفحهٔ دونیمه"</string>
@@ -1304,6 +1309,6 @@
<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>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"نور پسزمینه صفحهکلید"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"سطح %1$d از %2$d"</string>
- <string name="home_controls_dream_label" msgid="6567105701292324257">"کنترلهای لوازم خانگی"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"دسترسی سریع به کنترلهای لوازم خانگی بهعنوان محافظ صفحهنمایش"</string>
+ <string name="home_controls_dream_label" msgid="6567105701292324257">"کنترل خانه هوشمند"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"به کنترل خانه هوشمند بهعنوان محافظ صفحهنمایش دسترسی سریع دارید"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
index 9a6b1af..756b442 100644
--- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"خاموش"</item>
<item msgid="5137565285664080143">"روشن"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"دردسترس نیست"</item>
+ <item msgid="3079622119444911877">"خاموش"</item>
+ <item msgid="3028994095749238254">"روشن"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 55413a0..2935d2e 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -120,7 +120,7 @@
<string name="screenrecord_stop_label" msgid="72699670052087989">"Lopeta"</string>
<string name="screenrecord_share_label" msgid="5025590804030086930">"Jaa"</string>
<string name="screenrecord_save_title" msgid="1886652605520893850">"Näyttötallenne tallennettu"</string>
- <string name="screenrecord_save_text" msgid="3008973099800840163">"Napauta näyttääksesi"</string>
+ <string name="screenrecord_save_text" msgid="3008973099800840163">"Katso napauttamalla"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Virhe näyttötallenteen tallentamisessa"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Virhe näytön tallennuksen aloituksessa"</string>
<string name="issuerecord_title" msgid="286627115110121849">"Ongelman tallentaja"</string>
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Näytä kaikki"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Käytä Bluetoothia"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Yhdistetty"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Tallennettu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkaise yhteys"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivoi"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Laita automaattisesti päälle taas huomenna"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Ominaisuudet (esim. Quick Share ja Paikanna laite) ja laitteen sijainti käyttävät Bluetoothia"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth käynnistetään huomenna klo 5.00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Tallenna ongelma"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Aloita"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Lopeta"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Virheraportti"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Mitä osaa käyttökokemuksesta ongelma koski?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valitse ongelman tyyppi"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Näytön tallentaja"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Tavallinen"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Keskitaso"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Suuri"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuulolaitteet"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Kuulolaitteet"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Muodosta uusi laitepari"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Muodosta uusi laitepari klikkaamalla"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kumotaanko laitteen mikrofonin esto?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kumotaanko laitteen kameran esto?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kumotaanko laitteen kameran ja mikrofonin esto?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Vaihda näppäimistöasettelu"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"tai"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Tyhjennä hakulauseke"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Pikanäppäimet"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Pikanäppäimet"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Hae pikanäppäimiä"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Pikanäppäimiä ei löytynyt"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Järjestelmä"</string>
@@ -770,17 +775,17 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Avaa Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lukitusnäyttö"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Tee muistiinpano"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Järjestelmän monikäyttö"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Siirry jaettuun näyttöön (sovellus oikeanpuoleiseen näyttöön)"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Siirry jaettuun näyttöön (sovellus vasemmanpuoleiseen näyttöön)"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitaskaus"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Käytä jaettua näyttöä niin, että nyt käytettävä sovellus on oikealla"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Käytä jaettua näyttöä niin, että nyt käytettävä sovellus on vasemmalla"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Vaihda jaetusta näytöstä koko näyttöön"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Vaihda sovellukseen oikealla tai alapuolella jaetun näytön avulla"</string>
- <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Vaihda sovellukseen vasemmalla tai yläpuolella jaetun näytön avulla"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Vaihda sovellukseen oikealla tai alapuolella jaetussa näytössä"</string>
+ <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Vaihda sovellukseen vasemmalla tai yläpuolella jaetussa näytössä"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Jaetun näytön aikana: korvaa sovellus toisella"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Syöttötapa"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Vaihda seuraavaan kieleen"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Vaihda aiempaan kieleen"</string>
- <string name="input_access_emoji" msgid="8105642858900406351">"Emojin käyttö"</string>
+ <string name="input_access_emoji" msgid="8105642858900406351">"Emojien käyttö"</string>
<string name="input_access_voice_typing" msgid="7291201476395326141">"Puhekirjoituksen käyttö"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Sovellukset"</string>
<string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"Assistant"</string>
@@ -1194,7 +1199,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# sovellus on aktiivinen}other{# sovellusta aktiivisena}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Uutta tietoa"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiiviset sovellukset"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Nämä sovellukset ovat aktiivisia ja ne ovat käynnissä, vaikka et käyttäisi niitä. Näin sovellusten toimivuus paranee, mutta se voi vaikutta akunkestoon."</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Nämä sovellukset ovat aktiivisia ja käynnissä, vaikka et käyttäisi niitä. Näin sovellusten toimivuus paranee, mutta ne voivat vaikuttaa akunkestoon."</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Pysäytä"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Pysäytetty"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Valmis"</string>
@@ -1269,7 +1274,7 @@
<string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"Asenna työpuhelinsovellus"</string>
<string name="call_from_work_profile_close" msgid="5830072964434474143">"Peruuta"</string>
<string name="lock_screen_settings" msgid="6152703934761402399">"Muokkaa lukitusnäyttöä"</string>
- <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Avaa lukitus muokataksesi lukitusnäyttöä"</string>
+ <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Voit muokata lukitusnäyttöä, kun avaat lukituksen"</string>
<string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi-yhteys ei ole käytettävissä"</string>
<string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera estetty"</string>
<string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera ja mikrofoni estetty"</string>
diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
index f1e3e61..5ecc9595 100644
--- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Pois päältä"</item>
<item msgid="5137565285664080143">"Päällä"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Ei saatavilla"</item>
+ <item msgid="3079622119444911877">"Pois päältä"</item>
+ <item msgid="3028994095749238254">"Päällä"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-fr-feminine/strings.xml b/packages/SystemUI/res/values-fr-feminine/strings.xml
index ebdc3fb..16c16e1 100644
--- a/packages/SystemUI/res/values-fr-feminine/strings.xml
+++ b/packages/SystemUI/res/values-fr-feminine/strings.xml
@@ -20,6 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez pas dérangée par des sons ou des vibrations, hormis ceux des alarmes, des rappels, des événements et des appelants de votre choix. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string>
- <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangée par des sons ou des vibrations, hormis ceux des alarmes. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string>
+ <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangée par des sons ou des vibrations, hormis ceux des alarmes. Vous entendrez encore les sons que vous choisirez de jouer, comme la musique, les vidéos et les jeux."</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Heureux de vous revoir, Invitée"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-masculine/strings.xml b/packages/SystemUI/res/values-fr-masculine/strings.xml
index 6b94970..411af1f 100644
--- a/packages/SystemUI/res/values-fr-masculine/strings.xml
+++ b/packages/SystemUI/res/values-fr-masculine/strings.xml
@@ -20,6 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes, des rappels, des événements et des appelants de votre choix. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string>
- <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string>
+ <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes. Vous entendrez encore les sons que vous choisirez de jouer, comme la musique, les vidéos et les jeux."</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Heureux de vous revoir, Invité"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-neuter/strings.xml b/packages/SystemUI/res/values-fr-neuter/strings.xml
index e9d0191..4a4ac5a 100644
--- a/packages/SystemUI/res/values-fr-neuter/strings.xml
+++ b/packages/SystemUI/res/values-fr-neuter/strings.xml
@@ -20,6 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez pas dérangé·e par des sons ou des vibrations, hormis ceux des alarmes, des rappels, des événements et des appelants de votre choix. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string>
- <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangé·e par des sons ou des vibrations, hormis ceux des alarmes. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string>
+ <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangé·e par des sons ou des vibrations, hormis ceux des alarmes. Vous entendrez encore les sons que vous choisirez de jouer, comme la musique, les vidéos et les jeux."</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Heureux de vous revoir, Invité·e"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 272e00e..bd53d85 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Tout afficher"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Utiliser le Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connecté"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Déconnecter"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"Activer"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activer le Bluetooth automatiquement demain"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Les fonctionnalités comme le Partage rapide, Localiser mon appareil et la position de l\'appareil utilisent le Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Le Bluetooth s\'activera demain à 5 h"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Rapporter le problème"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Commencer"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Rapport de bogue"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quelle composante de l\'appareil a été affectée?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionner un type"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement écran"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Moyen"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Élevé"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Appareils auditifs"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquez ici pour associer un nouvel appareil"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Débloquer le microphone de l\'appareil?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Débloquer l\'appareil photo de l\'appareil?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Débloquer l\'appareil photo et le microphone?"</string>
@@ -448,10 +453,8 @@
<string name="button_to_remove_widget" msgid="3948204829181214098">"Retirer"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ajouter un widget"</string>
<string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Terminé"</string>
- <!-- no translation found for label_for_button_in_empty_state_cta (7314975555382055823) -->
- <skip />
- <!-- no translation found for title_for_empty_state_cta (6161654421223450530) -->
- <skip />
+ <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Ajouter des widgets"</string>
+ <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Accédez rapidement aux widgets de vos applications préférées sans déverrouiller votre tablette."</string>
<string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Autoriser n\'importe quel widget sur l\'écran de verrouillage?"</string>
<string name="button_text_to_open_settings" msgid="1987729256950941628">"Ouvrir les paramètres"</string>
<string name="work_mode_off_title" msgid="5794818421357835873">"Réactiver les applis pros?"</string>
@@ -553,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Cet appareil est géré par ton parent. Ton parent peut voir et gérer de l\'information, comme les applications que tu utilises, ta position et ton temps d\'utilisation des écrans."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"RPV"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Maintenu déverrouillé par TrustAgent"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"L\'appareil a été verrouillé, trop de tentatives d\'authentification"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"L\'appareil a été verrouillé : trop de tentatives d\'authentification"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Appareil verrouillé\nÉchec de l\'authentification"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Paramètres sonores"</string>
@@ -747,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Changer la disposition du clavier"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Effacez la requête de recherche"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Raccourcis"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Raccourcis-clavier"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Recherchez des raccourcis"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Aucun raccourci trouvé"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Système"</string>
@@ -772,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Ouvrir l\'Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Écran de verrouillage"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Prendre une note"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitâche du système"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Passer à l\'écran divisé avec l\'application actuelle à droite"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Passer à l\'écran divisé avec l\'application actuelle à gauche"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitâche"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Utiliser l\'Écran divisé avec l\'application actuelle à droite"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Utiliser l\'Écran divisé avec l\'application actuelle à gauche"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Passer de l\'écran divisé au plein écran"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passer à l\'application à droite ou en dessous avec l\'Écran divisé"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passer à l\'application à gauche ou au-dessus avec l\'Écran divisé"</string>
@@ -1307,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Accès rapide : domot. sous forme d\'Écran de veille"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Accès rapide : domotique sous forme d\'Écran de veille"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index dfea45a..52b763b 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Désactivée"</item>
<item msgid="5137565285664080143">"Activée"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Non accessible"</item>
+ <item msgid="3079622119444911877">"Désactivé"</item>
+ <item msgid="3028994095749238254">"Activé"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 41f9249..109e767 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Tout afficher"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Utiliser le Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connecté"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"dissocier"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activer"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Réactiver automatiquement demain"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Certaines fonctionnalités telles que Quick Share, Localiser mon appareil ou encore la position de l\'appareil utilisent le Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Le Bluetooth sera activé demain à 5h00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batterie"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Casque"</string>
@@ -286,7 +294,7 @@
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Économiseur d\'écran"</string>
- <string name="quick_settings_camera_label" msgid="5612076679385269339">"Accès à la caméra"</string>
+ <string name="quick_settings_camera_label" msgid="5612076679385269339">"Accès à l\'appareil photo"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Accès au micro"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponible"</string>
<string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bloqué"</string>
@@ -298,7 +306,7 @@
<string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Réseaux non disponibles"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Aucun réseau Wi-Fi disponible"</string>
<string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Activation…"</string>
- <string name="quick_settings_cast_title" msgid="2279220930629235211">"Diffusion de l\'écran"</string>
+ <string name="quick_settings_cast_title" msgid="2279220930629235211">"Diffusion écran"</string>
<string name="quick_settings_casting" msgid="1435880708719268055">"Diffusion"</string>
<string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"Appareil sans nom"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Aucun appareil disponible."</string>
@@ -344,12 +352,13 @@
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC désactivée"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"La technologie NFC est activée"</string>
- <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Enregistrement de l\'écran"</string>
+ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Enregistr. écran"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string>
<string name="qs_record_issue_label" msgid="8166290137285529059">"Enregistrer le problème"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Début"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Rapport de bug"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quel problème avez-vous rencontré avec votre appareil ?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionnez un type de problème"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement de l\'écran"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Moyen"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Élevé"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Prothèses auditives"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Appareils auditifs"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquer pour associer un nouvel appareil"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Débloquer le micro de l\'appareil ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Débloquer la caméra de l\'appareil ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Débloquer l\'appareil photo et le micro de l\'appareil ?"</string>
@@ -400,7 +405,7 @@
<string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activer/Désactiver l\'écran Récents"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes, des rappels, des événements et des appelants de votre choix. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string>
- <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string>
+ <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes. Vous entendrez encore les sons que vous choisirez de jouer, comme la musique, les vidéos et les jeux."</string>
<string name="zen_priority_customize_button" msgid="4119213187257195047">"Personnaliser"</string>
<string name="zen_silence_introduction_voice" msgid="853573681302712348">"Cette option permet de bloquer TOUS les sons et les vibrations, y compris pour les alarmes, la musique, les vidéos et les jeux. Vous pourrez encore passer des appels téléphoniques."</string>
<string name="zen_silence_introduction" msgid="6117517737057344014">"Cette option permet de bloquer TOUS les sons et les vibrations, y compris pour les alarmes, la musique, les vidéos et les jeux."</string>
@@ -637,7 +642,7 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Déverrouiller pour utiliser"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Problème de récupération de vos cartes. Réessayez plus tard"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
- <string name="qr_code_scanner_title" msgid="1938155688725760702">"Lecteur de code QR"</string>
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Lecteur code QR"</string>
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mise à jour"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
@@ -745,8 +750,8 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Changer disposition du clavier"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Effacer la requête de recherche"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Raccourcis"</string>
- <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Raccourcis de recherche"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Raccourcis clavier"</string>
+ <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Rechercher des raccourcis"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Aucun raccourci trouvé"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Système"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Saisie"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Ouvrir l\'Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Verrouiller l\'écran"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Créer une note"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitâche du système"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Passer en écran partagé avec l\'appli actuelle affichée à droite"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Passer en écran partagé avec l\'appli actuelle affichée à gauche"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitâche"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Utiliser l\'écran partagé avec l\'appli actuelle sur la droite"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Utiliser l\'écran partagé avec l\'appli actuelle sur la gauche"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Passer de l\'écran partagé au plein écran"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passez à l\'appli à droite ou en dessous avec l\'écran partagé"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passez à l\'appli à gauche ou au-dessus avec l\'écran partagé"</string>
@@ -1247,7 +1252,7 @@
<string name="home_quick_affordance_unavailable_configure_the_app" msgid="604424593994493281">"• Au moins un appareil ou un panneau de l\'appareil est disponible"</string>
<string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Sélectionnez une appli de notes par défaut pour utiliser le raccourci de prise de notes"</string>
<string name="keyguard_affordance_enablement_dialog_notes_app_action" msgid="6821710209675089470">"Sélectionner une appli"</string>
- <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Appuyez de manière prolongée sur raccourci"</string>
+ <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Appuyez de manière prolongée sur le raccourci"</string>
<string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Annuler"</string>
<string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"Changer d\'écran maintenant"</string>
<string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"Déplier le téléphone"</string>
@@ -1269,7 +1274,7 @@
<string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"Installer une appli professionnelle pour téléphoner"</string>
<string name="call_from_work_profile_close" msgid="5830072964434474143">"Annuler"</string>
<string name="lock_screen_settings" msgid="6152703934761402399">"Personnaliser écran verrouillage"</string>
- <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Déverrouiller pour personnaliser l\'écran de verrouillage"</string>
+ <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Déverrouillez pour personnaliser l\'écran de verrouillage"</string>
<string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi non disponible"</string>
<string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Caméra bloquée"</string>
<string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Caméra et micro bloqués"</string>
@@ -1284,7 +1289,7 @@
<string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Fermer"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Écran connecté"</string>
- <string name="privacy_dialog_title" msgid="7839968133469098311">"Micro et caméra"</string>
+ <string name="privacy_dialog_title" msgid="7839968133469098311">"Micro et appareil photo"</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>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index 34ccb75..23c124c 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -88,8 +88,8 @@
</string-array>
<string-array name="tile_states_color_correction">
<item msgid="2840507878437297682">"Indisponible"</item>
- <item msgid="1909756493418256167">"Désactivé"</item>
- <item msgid="4531508423703413340">"Activé"</item>
+ <item msgid="1909756493418256167">"Désactivée"</item>
+ <item msgid="4531508423703413340">"Activée"</item>
</string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Indisponible"</item>
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Désactivé"</item>
<item msgid="5137565285664080143">"Activé"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Non disponible"</item>
+ <item msgid="3079622119444911877">"Désactivé"</item>
+ <item msgid="3028994095749238254">"Activé"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 189f048..06c984a 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver todo"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Estableceuse a conexión"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gardouse"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver activar automaticamente mañá"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"As funcións como Quick Share, Localizar o meu dispositivo ou a de localización do dispositivo utilizan o Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"O Bluetooth activarase mañá ás 05:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Rexistrar problema"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Deter"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe de erros"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cal foi o problema na experiencia co dispositivo?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona o tipo de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravación de pant."</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Nivel estándar"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Nivel medio"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Nivel alto"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular un dispositivo novo"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic para vincular un novo dispositivo"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Queres desbloquear o micrófono do dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Queres desbloquear a cámara do dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Queres desbloquear a cámara e o micrófono do dispositivo?"</string>
@@ -632,7 +637,7 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_empty_state_label" msgid="7776761245237530394">"Configura un método de pago para comprar de xeito máis rápido e seguro co teléfono"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Amosar todo"</string>
- <string name="wallet_secondary_label_no_card" msgid="8488069304491125713">"Tocar para abrir"</string>
+ <string name="wallet_secondary_label_no_card" msgid="8488069304491125713">"Toca para abrir"</string>
<string name="wallet_secondary_label_updating" msgid="5726130686114928551">"Actualizando"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Produciuse un problema ao obter as tarxetas. Téntao de novo máis tarde"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Cambiar deseño do teclado"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Borrar a busca"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Atallos"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Atallos de teclado"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Buscar atallos"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Non se atoparon atallos"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir Asistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Pantalla de bloqueo"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Crear nota"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitarefa do sistema"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Activar pantalla dividida con esta aplicación no lado dereito"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Activar pantalla dividida con esta aplicación no lado esquerdo"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarefa"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar pantalla dividida coa aplicación actual na dereita"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar pantalla dividida coa aplicación actual na esquerda"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Cambiar de pantalla dividida a pantalla completa"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Cambiar á aplicación da dereita ou de abaixo coa pantalla dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar á aplicación da esquerda ou de arriba coa pantalla dividida"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controis domóticos"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Controis domóticos como protector de pantalla"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Usa os controis domóticos como protector de pantalla"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
index de8ee63..03b934e 100644
--- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Desactivado"</item>
<item msgid="5137565285664080143">"Activado"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Non dispoñibles"</item>
+ <item msgid="3079622119444911877">"Desactivados"</item>
+ <item msgid="3028994095749238254">"Activados"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 54312e1..c9a5662 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"તમામ જુઓ"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"બ્લૂટૂથનો ઉપયોગ કરો"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"કનેક્ટેડ છે"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"સાચવેલું"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ડિસ્કનેક્ટ કરો"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"સક્રિય કરો"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"આવતીકાલે ફરીથી ઑટોમૅટિક રીતે ચાલુ કરો"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ક્વિક શેર, Find My Device અને ડિવાઇસના લોકેશન જેવી સુવિધાઓ બ્લૂટૂથનો ઉપયોગ કરે છે"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"બ્લૂટૂથ આવતીકાલે સવારે 5 વાગ્યે ચાલુ થશે"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> બૅટરી"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string>
@@ -333,7 +341,7 @@
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"સૂર્યોદય સુધી"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"<xliff:g id="TIME">%s</xliff:g> વાગ્યે"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> સુધી"</string>
- <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"ઘેરી થીમ"</string>
+ <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"ડાર્ક થીમ"</string>
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"બૅટરી સેવર"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"સૂર્યાસ્ત વખતે"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"સૂર્યોદય સુધી"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"રેકોર્ડિંગમાં સમસ્યા"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"શરૂ કરો"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"રોકો"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"બગ રિપોર્ટ"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ડિવાઇસ સંબંધી તમારા અનુભવના કયા ભાગને અસર થઈ હતી?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"સમસ્યાનો પ્રકાર પસંદ કરો"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"સ્ક્રીન રેકોર્ડ કરો"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"સ્ટૅન્ડર્ડ"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"મધ્યમ"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"વધુ"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"નવા ડિવાઇસ સાથે જોડાણ કરવા માટે ક્લિક કરો"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ડિવાઇસના માઇક્રોફોનને અનબ્લૉક કરીએ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ડિવાઇસના કૅમેરાને અનબ્લૉક કરીએ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ડિવાઇસના કૅમેરા અને માઇક્રોફોનને અનબ્લૉક કરીએ?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"કીબોર્ડ લેઆઉટ સ્વિચ કરો"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"અથવા"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"શોધ ક્વેરી સાફ કરો"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"શૉર્ટકટ"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"કીબોર્ડ શૉર્ટકટ"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"શૉર્ટકટ શોધો"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"કોઈ શૉર્ટકટ મળ્યો નથી"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"સિસ્ટમ"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant ખોલો"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"લૉક સ્ક્રીન"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"નોંધ લો"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"સિસ્ટમ દ્વારા એકથી વધુ કાર્યો કરવા"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"જમણી બાજુ પર હાલની ઍપ સાથે વિભાજિત સ્ક્રીનમાં દાખલ થાઓ"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"ડાબી બાજુ પર હાલની ઍપ સાથે વિભાજિત સ્ક્રીનમાં દાખલ થાઓ"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"એકસાથે એકથી વધુ કાર્યો કરવા"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"જમણી બાજુએ વર્તમાન ઍપ સાથે વિભાજિત સ્ક્રીનનો ઉપયોગ કરો"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"ડાબી બાજુએ વર્તમાન ઍપ સાથે વિભાજિત સ્ક્રીનનો ઉપયોગ કરો"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"વિભાજિત સ્ક્રીનથી પૂર્ણ સ્ક્રીન પર સ્વિચ કરો"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરતી વખતે જમણી બાજુ કે નીચેની ઍપ પર સ્વિચ કરો"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરતી વખતે ડાબી બાજુની કે ઉપરની ઍપ પર સ્વિચ કરો"</string>
diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
index c6a86e5..5c4a478 100644
--- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"બંધ"</item>
<item msgid="5137565285664080143">"ચાલુ"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"અનુપલબ્ધ"</item>
+ <item msgid="3079622119444911877">"બંધ છે"</item>
+ <item msgid="3028994095749238254">"ચાલુ છે"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index ee3e40b..b2b2037 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"सभी देखें"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लूटूथ इस्तेमाल करें"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट है"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव किया गया"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिसकनेक्ट करें"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"चालू करें"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"कल फिर से अपने-आप चालू हो जाएगा"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"क्विक शेयर, Find My Device, और डिवाइस की जगह की जानकारी का पता लगाने जैसी सुविधाएं, ब्लूटूथ का इस्तेमाल करती हैं"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"कल सुबह 5 बजे ब्लूटूथ चालू हो जाएगा"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बैटरी"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"समस्या रिकॉर्ड करें"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"शुरू करें"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"रोकें"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"गड़बड़ी की रिपोर्ट"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"आपके डिवाइस की कौनसी सुविधा पर असर पड़ा था?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्या का टाइप चुनें"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रीन रिकॉर्डर"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"स्टैंडर्ड"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"सामान्य"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"ज़्यादा"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"कान की मशीनें"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"कान की मशीनें"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नया डिवाइस जोड़ें"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नया डिवाइस जोड़ने के लिए क्लिक करें"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"क्या आपको डिवाइस का माइक्रोफ़ोन अनब्लॉक करना है?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"क्या आपको डिवाइस का कैमरा अनब्लॉक करना है?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"क्या आप डिवाइस का कैमरा और माइक्रोफ़ोन अनब्लॉक करना चाहते हैं?"</string>
@@ -552,7 +557,7 @@
<string name="legacy_vpn_name" msgid="4174223520162559145">"वीपीएन"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent की वजह से अनलॉक रखा गया है"</string>
<string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"कई बार पुष्टि करने की कोशिश की वजह से, डिवाइस लॉक है"</string>
- <string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"डिवाइस लॉक है\nपुष्टि नहीं की जा सकी."</string>
+ <string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"डिवाइस लॉक हो गया है\nपुष्टि नहीं की जा सकी"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"साउंड सेटिंग"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ऑडियो-वीडियो से अपने-आप कैप्शन बनना"</string>
@@ -678,7 +683,7 @@
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>स्थिति:</b> रैंकिंग में नीचे किया गया"</string>
<string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"यह कई तरीकों से दिखती है, जैसे कि लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो के तौर पर और बातचीत वाली सूचनाओं में सबसे ऊपर"</string>
<string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"यह कई तरीकों से दिखती है, जैसे कि बातचीत वाली सूचनाओं में सबसे ऊपर, बबल के तौर पर, और लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो के तौर पर"</string>
- <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"यह कई तरीकों से दिखती है, जैसे कि लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो के तौर पर और बातचीत वाली सूचनाओं में सबसे ऊपर. साथ ही, इसकी वजह से, \'परेशान न करें\' सुविधा में भी रुकावट आती है"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"यह लॉक स्क्रीन पर, बातचीत वाली सूचनाओं में सबसे ऊपर और प्रोफ़ाइल फ़ोटो के साथ दिखती है. साथ ही, यह \'परेशान न करें\' मोड को बायपास कर सकती है."</string>
<string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"यह कई तरीकों से दिखती है, जैसे कि बातचीत वाली सूचनाओं में सबसे ऊपर, बबल के तौर पर, और लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो के तौर पर. साथ ही, यह \'परेशान न करें\' मोड को बायपास कर सकती है"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"प्राथमिकता"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर बातचीत की सुविधाएं काम नहीं करतीं"</string>
@@ -745,12 +750,12 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"कीबोर्ड लेआउट बदलें"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"या"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"सर्च क्वेरी साफ़ करें"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"शॉर्टकट"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"कीबोर्ड शॉर्टकट"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"शॉर्टकट खोजें"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"कोई शॉर्टकट नहीं मिला"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"सिस्टम"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"इनपुट"</string>
- <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"खुले हुए ऐप्लिकेशन"</string>
+ <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"ऐप्लिकेशन खोलने के लिए"</string>
<string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"मौजूदा ऐप्लिकेशन"</string>
<string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"खोज के नतीजे दिखाए जा रहे हैं"</string>
<string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"सिस्टम के शॉर्टकट दिखाए जा रहे हैं"</string>
@@ -759,8 +764,8 @@
<string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"मौजूदा ऐप्लिकेशन के लिए शॉर्टकट दिखाए जा रहे हैं"</string>
<string name="group_system_access_notification_shade" msgid="1619028907006553677">"सूचनाएं देखने के लिए"</string>
<string name="group_system_full_screenshot" msgid="5742204844232667785">"स्क्रीनशॉट लेने के लिए"</string>
- <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"शॉर्टकट दिखाने के लिए"</string>
- <string name="group_system_go_back" msgid="2730322046244918816">"वापस पीछे जाने के लिए"</string>
+ <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"शॉर्टकट देखने के लिए"</string>
+ <string name="group_system_go_back" msgid="2730322046244918816">"वापस जाने के लिए"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"होम स्क्रीन पर जाने के लिए"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखने के लिए"</string>
<string name="group_system_cycle_forward" msgid="5478663965957647805">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन के अगले पेज पर जाने के लिए"</string>
@@ -768,11 +773,11 @@
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"ऐप्लिकेशन की सूची खोलने के लिए"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"सेटिंग खोलने के लिए"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Google Assistant खोलने के लिए"</string>
- <string name="group_system_lock_screen" msgid="7391191300363416543">"लॉक स्क्रीन"</string>
- <string name="group_system_quick_memo" msgid="3764560265935722903">"नोट करें"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"सिस्टम मल्टीटास्किंग"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"स्प्लिट स्क्रीन का इस्तेमाल करके, मौजूदा ऐप्लिकेशन को दाईं ओर ले जाएं"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"स्प्लिट स्क्रीन का इस्तेमाल करके, मौजूदा ऐप्लिकेशन को बाईं ओर ले जाएं"</string>
+ <string name="group_system_lock_screen" msgid="7391191300363416543">"स्क्रीन लॉक करने के लिए"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"नोट करने के लिए"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"मल्टीटास्किंग (एक साथ कई काम करना)"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"मौजूदा ऐप्लिकेशन को दाईं ओर दिखाने वाली स्प्लिट स्क्रीन इस्तेमाल करें"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"मौजूदा ऐप्लिकेशन को बाईं ओर दिखाने वाली स्प्लिट स्क्रीन इस्तेमाल करें"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"स्प्लिट स्क्रीन से फ़ुल स्क्रीन मोड पर स्विच करने के लिए"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रीन इस्तेमाल करते समय दाईं ओर या नीचे के ऐप पर स्विच करें"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रीन इस्तेमाल करते समय बाईं ओर या ऊपर के ऐप पर स्विच करें"</string>
@@ -829,7 +834,7 @@
<string name="right_keycode" msgid="2480715509844798438">"दायां कुंजी कोड"</string>
<string name="left_icon" msgid="5036278531966897006">"बायां आइकॉन"</string>
<string name="right_icon" msgid="1103955040645237425">"दायां आइकॉन"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"टाइल जोड़ने के लिए दबाएं और खींचें"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"टाइल जोड़ने के लिए दबाकर खींचे और छोड़ें"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"टाइल का क्रम फिर से बदलने के लिए उन्हें दबाकर रखें और खींचें"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"हटाने के लिए यहां खींचें और छोड़ें"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"आपके पास कम से कम <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> टाइलें होनी चाहिए"</string>
@@ -1123,7 +1128,7 @@
<string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string>
<string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर को क्लिपबोर्ड पर कॉपी किया गया."</string>
<string name="basic_status" msgid="2315371112182658176">"ऐसी बातचीत जिसमें इंटरैक्शन डेटा मौजूद नहीं है"</string>
- <string name="select_conversation_title" msgid="6716364118095089519">"बातचीत विजेट"</string>
+ <string name="select_conversation_title" msgid="6716364118095089519">"बातचीत वाला विजेट"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"किसी बातचीत को होम स्क्रीन पर जोड़ने के लिए, उस बातचीत पर टैप करें"</string>
<string name="no_conversations_text" msgid="5354115541282395015">"हाल ही में हुई बातचीत यहां दिखेंगी"</string>
<string name="priority_conversations" msgid="3967482288896653039">"अहम बातचीत"</string>
@@ -1194,7 +1199,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ऐप्लिकेशन चालू है}one{# ऐप्लिकेशन चालू है}other{# ऐप्लिकेशन चालू हैं}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"नई जानकारी"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ये ऐप्लिकेशन चालू हैं"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ये ऐप्लिकेशन चालू हैं और आपके इस्तेमाल न करने पर भी चल रहे हैं. इससे, ये बेहतर तरीके से फ़ंक्शन करते हैं. हालांकि, इससे बैटरी लाइफ़ पर भी असर पड़ सकता है."</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ये ऐप्लिकेशन चालू हैं और आपके इस्तेमाल न करने पर भी चल रहे हैं. इससे ये बेहतर तरीके से काम कर पाते हैं. हालांकि, बैटरी लाइफ़ पर इसका असर पड़ सकता है."</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"बंद करें"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"बंद है"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"हो गया"</string>
@@ -1286,7 +1291,7 @@
<string name="connected_display_icon_desc" msgid="6373560639989971997">"डिसप्ले कनेक्ट किया गया"</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-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
index 0cb06c0..b89eeb3 100644
--- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"बंद है"</item>
<item msgid="5137565285664080143">"चालू है"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"उपलब्ध नहीं हैं"</item>
+ <item msgid="3079622119444911877">"बंद हैं"</item>
+ <item msgid="3028994095749238254">"चालू हैं"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index a1d885a..64c35b8 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -119,7 +119,7 @@
<string name="screenrecord_taps_label" msgid="1595690528298857649">"Prikaz dodira na zaslonu"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Zaustavi"</string>
<string name="screenrecord_share_label" msgid="5025590804030086930">"Dijeli"</string>
- <string name="screenrecord_save_title" msgid="1886652605520893850">"Snimanje zaslona spremljeno"</string>
+ <string name="screenrecord_save_title" msgid="1886652605520893850">"Snimanje zaslona je spremljeno"</string>
<string name="screenrecord_save_text" msgid="3008973099800840163">"Dodirnite za prikaz"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Pogreška prilikom spremanja snimke zaslona"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Pogreška prilikom pokretanja snimanja zaslona"</string>
@@ -270,19 +270,27 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Pogledajte sve"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Spremljeno"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekini vezu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviraj"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Značajke kao što su brzo dijeljenje, Pronađi moj uređaj i lokacija uređaja upotrebljavaju Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth će se uključiti sutra u 5:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Unos"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušna pomagala"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. zakretanje"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko zakretanje"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko zakretanje zaslona"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Čuvar zaslona"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Zabilježi poteškoću"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Pokreni"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavi"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvješće o pogrešci"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji dio doživljaja na uređaju to utjecalo?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje zaslona"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardni"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Srednji"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Visoki"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušna pomagala"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni uređaji"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparivanje novog uređaja"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili novi uređaj"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite li deblokirati mikrofon uređaja?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite li deblokirati kameru uređaja?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite li deblokirati kameru i mikrofon uređaja?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Promjena rasporeda tipkovnice"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ili"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Brisanje upita za pretraživanje"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Prečaci"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Tipkovni prečaci"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pretražite prečace"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nema nijednog prečaca"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sustav"</string>
@@ -757,29 +762,29 @@
<string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Prikazuju se prečaci za unos"</string>
<string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Prikazuju se prečaci koji otvaraju aplikacije"</string>
<string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Prikazuju se prečaci za trenutačnu aplikaciju"</string>
- <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Prikaži obavijesti"</string>
- <string name="group_system_full_screenshot" msgid="5742204844232667785">"Snimi zaslon"</string>
+ <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Prikaz obavijesti"</string>
+ <string name="group_system_full_screenshot" msgid="5742204844232667785">"Snimanje zaslona"</string>
<string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Prikaži prečace"</string>
<string name="group_system_go_back" msgid="2730322046244918816">"Natrag"</string>
- <string name="group_system_access_home_screen" msgid="4130366993484706483">"Idi na početni zaslon"</string>
- <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Prikaži nedavne aplikacije"</string>
- <string name="group_system_cycle_forward" msgid="5478663965957647805">"Kruži unaprijed kroz nedavne aplikacije"</string>
- <string name="group_system_cycle_back" msgid="8194102916946802902">"Kruži unatrag kroz nedavne aplikacije"</string>
- <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Otvori popis aplikacija"</string>
- <string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvori postavke"</string>
- <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvori Asistenta"</string>
+ <string name="group_system_access_home_screen" msgid="4130366993484706483">"Otvaranje početnog zaslona"</string>
+ <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Prikaz nedavnih aplikacija"</string>
+ <string name="group_system_cycle_forward" msgid="5478663965957647805">"Kruženje unaprijed kroz nedavne aplikacije"</string>
+ <string name="group_system_cycle_back" msgid="8194102916946802902">"Kruženje unatrag kroz nedavne aplikacije"</string>
+ <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Otvaranje popisa aplikacija"</string>
+ <string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvaranje postavki"</string>
+ <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvaranje asistenta"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zaključavanje zaslona"</string>
- <string name="group_system_quick_memo" msgid="3764560265935722903">"Zapišite bilješku"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Obavljanje više zadataka sustava"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Otvori podijeljeni zaslon s trenutačnom aplikacijom s desne strane"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Otvori podijeljeni zaslon s trenutačnom aplikacijom s lijeve strane"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Prijeđi s podijeljenog zaslona na cijeli zaslon"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Pisanje bilješke"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Obavljanje više zadataka"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Koristite podijeljeni zaslon s trenutačnom aplikacijom s desne strane"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Koristite podijeljeni zaslon s trenutačnom aplikacijom s lijeve strane"</string>
+ <string name="system_multitasking_full_screen" msgid="336048080383640562">"Prelazak s podijeljenog zaslona na cijeli zaslon"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prijeđite na aplikaciju zdesna ili ispod uz podijeljeni zaslon"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Prijeđite na aplikaciju slijeva ili iznad uz podijeljeni zaslon"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Tijekom podijeljenog zaslona: zamijeni aplikaciju drugom"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string>
- <string name="input_switch_input_language_next" msgid="3782155659868227855">"Prijeđi na sljedeći jezik"</string>
- <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prijeđi na prethodni jezik"</string>
+ <string name="input_switch_input_language_next" msgid="3782155659868227855">"Prelazak na sljedeći jezik"</string>
+ <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prelazak na prethodni jezik"</string>
<string name="input_access_emoji" msgid="8105642858900406351">"Pristupanje emojijima"</string>
<string name="input_access_voice_typing" msgid="7291201476395326141">"Pristupanje unosu teksta govorom"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Aplikacije"</string>
@@ -1297,13 +1302,13 @@
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"Koristi telefonski poziv"</string>
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"Nedavno korišteno tijekom telefonskog poziva"</string>
<string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Koristi: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Nedavno koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Nedavno je koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Koristi: <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">"Nedavno koristila aplikacija <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">"Koristi: <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">"Nedavno koristila aplikacija <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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string>
- <string name="home_controls_dream_label" msgid="6567105701292324257">"Upr. kuć. uređ."</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Brzo pristupajte Upr. kuć. uređ. kao čuvaru zasl."</string>
+ <string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Brzo upravljajte uređajima putem čuvara zaslona"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
index e09cab5..df0b786 100644
--- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Isključeno"</item>
<item msgid="5137565285664080143">"Uključeno"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Nedostupno"</item>
+ <item msgid="3079622119444911877">"Isključeno"</item>
+ <item msgid="3028994095749238254">"Uključeno"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index baea240..96c3e83 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Összes megtekintése"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth használata"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Csatlakozva"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Mentve"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"leválasztás"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiválás"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatikus visszakapcsolás holnap"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Az olyan funkciók, mint a Quick Share, a Készülékkereső és az eszköz helyadatai Bluetootht használnak"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"A Bluetooth bekapcsol holnap reggel 5-kor"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkumulátor: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Probléma rögzítése"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Indítás"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Leállítás"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Hibajelentés"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Az eszközhasználati élmény mely része érintett?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problématípus kiválasztása"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Képernyőrögzítés"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Normál"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Közepes"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Nagy"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hallókészülékek"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hallókészülékek"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Új eszköz párosítása"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kattintson új eszköz párosításához"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Feloldja az eszköz mikrofonjának letiltását?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Feloldja az eszköz kamerájának letiltását?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Feloldja az eszköz kamerájának és mikrofonjának letiltását?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Billentyűzetkiosztás váltása"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"vagy"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Keresőkifejezés törlése"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Billentyűparancsok"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Billentyűparancsok"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Billentyűparancs keresése"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nincs billentyűparancs"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Rendszer"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"A Segéd megnyitása"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lezárási képernyő"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Jegyzetelés"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Rendszermultitasking"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Osztott képernyő aktiválása; az aktuális alkalmazás kerüljön jobbra"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Osztott képernyő aktiválása; az aktuális alkalmazás kerüljön balra"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Osztott képernyő használata, a jelenlegi alkalmazás legyen jobb oldalt"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Osztott képernyő használata, a jelenlegi alkalmazás legyen bal oldalt"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Váltás osztott képernyőről teljes képernyőre"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Váltás a jobb oldalt, illetve lent lévő appra osztott képernyő esetén"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Váltás a bal oldalt, illetve fent lévő appra osztott képernyő esetén"</string>
diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
index 8f75dc6..bbd6bc0 100644
--- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Ki"</item>
<item msgid="5137565285664080143">"Be"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Nem áll rendelkezésre"</item>
+ <item msgid="3079622119444911877">"Ki"</item>
+ <item msgid="3028994095749238254">"Be"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 3f88746..00c3318 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Տեսնել բոլորը"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Միացնել Bluetooth-ը"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Միացված է"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Պահված է"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"անջատել"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ակտիվացնել"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Վաղը նորից ավտոմատ միացնել"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Գործառույթները, ինչպիսիք են Quick Share-ը, «Գտնել իմ սարքը» գործառույթը և սարքի տեղորոշումը, օգտագործում են Bluetooth-ը"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth-ը կմիանա վաղը՝ ժամը 05։00-ին"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Ձայնագրել"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Սկսել"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Կանգնեցնել"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Հաղորդում սխալի մասին"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Սարքի ո՞ր մասի հետ է կապված խնդիրը։"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Ընտրեք խնդրի տեսակը"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Էկրանի տեսագրում"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Սովորական"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Միջին"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Բարձր"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Լսողական սարքեր"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Լսողական սարքեր"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Նոր սարքի զուգակցում"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Սեղմեք՝ նոր սարք զուգակցելու համար"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Արգելահանե՞լ սարքի խոսափողը"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Արգելահանե՞լ սարքի տեսախցիկը"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Արգելահանե՞լ սարքի տեսախցիկը և խոսափողը"</string>
@@ -720,7 +725,7 @@
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Բացատ"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Մուտք"</string>
- <string name="keyboard_key_backspace" msgid="4095278312039628074">"Հետշարժ"</string>
+ <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Նվագարկում/դադար"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"Կանգնեցնել"</string>
<string name="keyboard_key_media_next" msgid="8502476691227914952">"Հաջորդը"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Դասավորության փոխարկում"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"կամ"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Ջնջել որոնման հարցումը"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Դյուրանցումներ"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Ստեղնային դյուրանցումներ"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Դյուրանցումների որոնում"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Դյուրանցումներ չեն գտնվել"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Համակարգ"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Բացել Օգնականը"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Կողպէկրան"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Ստեղծել նշում"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Համակարգի բազմախնդրության ռեժիմ"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Միացնել էկրանի տրոհումը՝ ընթացիկ հավելվածն աջ կողմում"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Միացնել էկրանի տրոհումը՝ ընթացիկ հավելվածը ձախ կողմում"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Բազմախնդրություն"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Աջ մասում օգտագործեք տրոհված էկրանն այս հավելվածի հետ"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Ձախ մասում օգտագործեք տրոհված էկրանն այս հավելվածի հետ"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Տրոհված էկրանից անցնել լիաէկրան ռեժիմ"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Անցեք աջ կողմի կամ ներքևի հավելվածին տրոհված էկրանի միջոցով"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Անցեք աջ կողմի կամ վերևի հավելվածին տրոհված էկրանի միջոցով"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Հետին լուսավորությամբ ստեղնաշար"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d՝ %2$d-ից"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարման տարրեր"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Օգտագործեք տան կառավարման տարրերը որպես էկրանապահ"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Տան կառավարման տարրերը դարձրեք էկրանապահ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
index 118915d..eb77ccf 100644
--- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Անջատված է"</item>
<item msgid="5137565285664080143">"Միացված է"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Հասանելի չէ"</item>
+ <item msgid="3079622119444911877">"Անջատված է"</item>
+ <item msgid="3028994095749238254">"Միացված է"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 0b07a32..826fc1a4 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -119,7 +119,7 @@
<string name="screenrecord_taps_label" msgid="1595690528298857649">"Tampilkan lokasi sentuhan pada layar"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Stop"</string>
<string name="screenrecord_share_label" msgid="5025590804030086930">"Bagikan"</string>
- <string name="screenrecord_save_title" msgid="1886652605520893850">"Perekaman layar disimpan"</string>
+ <string name="screenrecord_save_title" msgid="1886652605520893850">"Rekaman layar disimpan"</string>
<string name="screenrecord_save_text" msgid="3008973099800840163">"Ketuk untuk melihat"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Terjadi error saat menyimpan rekaman layar"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Terjadi error saat memulai perekaman layar"</string>
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Lihat semua"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Gunakan Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Terhubung"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan koneksi"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Otomatis aktifkan lagi besok"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Fitur seperti Quick Share, Temukan Perangkat Saya, dan lokasi perangkat menggunakan Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth akan diaktifkan besok pada pukul 05.00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Mencatat Masalah"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Mulai"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Berhenti"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Laporan Bug"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bagian pengalaman perangkat mana yang terpengaruh?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Perekaman layar"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standar"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Sedang"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Tinggi"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Alat bantu dengar"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Alat bantu dengar"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sambungkan perangkat baru"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menyambungkan perangkat baru"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Berhenti memblokir mikrofon perangkat?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Berhenti memblokir kamera perangkat?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Berhenti memblokir kamera dan mikrofon perangkat?"</string>
@@ -594,7 +599,7 @@
<string name="stream_accessibility" msgid="3873610336741987152">"Aksesibilitas"</string>
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Dering"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Getar"</string>
- <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Nonaktifkan"</string>
+ <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Bisukan"</string>
<string name="media_device_cast" msgid="4786241789687569892">"Transmisi"</string>
<string name="stream_notification_unavailable" msgid="4313854556205836435">"Tidak tersedia - Volume dering dibisukan"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"Tidak tersedia - Fitur Jangan Ganggu aktif"</string>
@@ -638,7 +643,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"Terjadi error saat mendapatkan kartu Anda, coba lagi nanti"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setelan layar kunci"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"Pemindai kode QR"</string>
- <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mengupdate"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Update berlangsung"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode pesawat"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar alarm berikutnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -745,8 +750,8 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Ganti tata letak keyboard"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"atau"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Hapus kueri penelusuran"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Pintasan"</string>
- <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pintasan penelusuran"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Pintasan Keyboard"</string>
+ <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Telusuri pintasan"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Tidak ditemukan pintasan"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistem"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Input"</string>
@@ -763,16 +768,16 @@
<string name="group_system_go_back" msgid="2730322046244918816">"Kembali"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"Buka layar utama"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"Lihat aplikasi terbaru"</string>
- <string name="group_system_cycle_forward" msgid="5478663965957647805">"Menavigasi maju pada aplikasi terbaru"</string>
- <string name="group_system_cycle_back" msgid="8194102916946802902">"Menavigasi mundur pada aplikasi terbaru"</string>
+ <string name="group_system_cycle_forward" msgid="5478663965957647805">"Berpindah-pindah aplikasi terbaru ke arah kanan"</string>
+ <string name="group_system_cycle_back" msgid="8194102916946802902">"Berpindah-pindah aplikasi terbaru ke arah kiri"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Buka daftar aplikasi"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Buka setelan"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Buka Asisten"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kunci layar"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Buat catatan"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking sistem"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Masuk ke layar terpisah dengan aplikasi saat ini ke RHS"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Masuk ke layar terpisah dengan aplikasi saat ini ke LHS"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gunakan layar terpisah dengan aplikasi saat ini di sebelah kanan"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gunakan layar terpisah dengan aplikasi saat ini di sebelah kiri"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Beralih dari layar terpisah ke layar penuh"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Beralih ke aplikasi di bagian kanan atau bawah saat menggunakan layar terpisah"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Beralih ke aplikasi di bagian kiri atau atas saat menggunakan layar terpisah"</string>
diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml
index bd429c1..a415f64 100644
--- a/packages/SystemUI/res/values-in/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Nonaktif"</item>
<item msgid="5137565285664080143">"Aktif"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Tidak tersedia"</item>
+ <item msgid="3079622119444911877">"Nonaktif"</item>
+ <item msgid="3028994095749238254">"Aktif"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 56ea0d5..1a0b3f2 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Sjá allt"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Nota Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Tengt"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Vistað"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"aftengja"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"virkja"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Kveikja sjálfkrafa aftur á morgun"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Eiginleikar á borð við flýtideilingu, „Finna tækið mitt“ og staðsetningu tækis nota Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Kveikt verður á Bluetooth á morgun kl. 05:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> rafhlöðuhleðsla"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Vandamál tengt upptöku"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Byrja"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stöðva"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Villutilkynning"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvað í tækjaupplifuninni varð fyrir áhrifum?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Veldu gerð vandamáls"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjáupptaka"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Staðlað"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Miðlungs"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Mikið"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Heyrnartæki"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Heyrnartæki"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Para nýtt tæki"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Smelltu til að para nýtt tæki"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Opna fyrir hljóðnema tækisins?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Opna fyrir myndavél tækisins?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Opna fyrir myndavél og hljóðnema tækisins?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Skipta um lyklaskipan"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"eða"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Hreinsa leitarfyrirspurn"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Flýtileiðir"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Flýtilyklar"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Leita að flýtileiðum"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Engar flýtileiðir fundust"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Kerfi"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Opna Hjálpara"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lásskjár"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Skrifa glósu"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Fjölvinnsla kerfis"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Opna skjáskiptingu hægra megin með núverandi forriti"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Opna skjáskiptingu vinstra megin með núverandi forriti"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Fjölvinnsla"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Notaðu skjáskiptingu með núverandi forriti til hægri"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Notaðu skjáskiptingu með núverandi forriti til vinstri"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Skipta úr skjáskiptingu yfir á allan skjáinn"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skiptu í forrit til hægri eða fyrir neðan þegar skjáskipting er notuð"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skiptu í forrit til vinstri eða fyrir ofan þegar skjáskipting er notuð"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Baklýsing lyklaborðs"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Stig %1$d af %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Heimastýringar"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Fáðu skjótan aðgang að heimastýringum með því að stilla þær sem skjávara"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Fáðu skjótan aðgang að heimastýringum sem skjávara"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml
index abdc3e7..c9befd6 100644
--- a/packages/SystemUI/res/values-is/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Slökkt"</item>
<item msgid="5137565285664080143">"Kveikt"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Ekki tiltækt"</item>
+ <item msgid="3079622119444911877">"Slökkt"</item>
+ <item msgid="3028994095749238254">"Kveikt"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 08f07ef..d7e5cdc 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Visualizza tutti"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usa Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Dispositivo connesso"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Dispositivo salvato"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnetti"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"attiva"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Riattiva automaticamente domani"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funzionalità come Quick Share, Trova il mio dispositivo e la posizione del dispositivo usano il Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Il Bluetooth verrà attivato domani alle 05:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auricolare"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Registra problema"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Avvia"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Interrompi"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Segnalazione di bug"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quali problemi ha l\'esperienza del dispositivo?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleziona il tipo di problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regis. dello schermo"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Medio"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Alto"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Apparecchi acustici"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Protesi uditive"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Accoppia nuovo dispositivo"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic per accoppiare un nuovo dispositivo"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vuoi sbloccare il microfono del dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vuoi sbloccare la fotocamera del dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vuoi sbloccare la fotocamera e il microfono del dispositivo?"</string>
@@ -550,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Questo dispositivo è gestito da uno dei tuoi genitori, il quale può visualizzare e gestire informazioni come le app che usi, la tua posizione e il tuo tempo di utilizzo."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Sbloccato da TrustAgent"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Dispositivo bloccato, troppi tentativi di autenticazione"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Dispositivo bloccato: troppi tentativi di autenticazione"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Dispositivo bloccato\nAutenticazione non riuscita"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Impostazioni audio"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Cambia layout della tastiera"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"oppure"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Cancella la query di ricerca"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Scorciatoie"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Scorciatoie da tastiera"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Cerca scorciatoie"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Scorciatoie non trovate"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Apri l\'assistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Blocca lo schermo"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Scrivi una nota"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking di sistema"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Attiva lo schermo diviso con l\'app corrente a destra"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Attiva lo schermo diviso con l\'app corrente a sinistra"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Utilizza schermo diviso con l\'app corrente a destra"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Utilizza schermo diviso con l\'app corrente a sinistra"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Passa da schermo diviso a schermo intero"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passa all\'app a destra o sotto mentre usi lo schermo diviso"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passa all\'app a sinistra o sopra mentre usi lo schermo diviso"</string>
@@ -1304,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Accedi ai controlli della casa dal salvaschermo"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Accedi rapidamente ai controlli della casa dal salvaschermo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 24ec785..bed2fc7 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"הצגת הכול"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"מחובר"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ניתוק"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"הפעלה"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"החיבור יופעל שוב אוטומטית מחר"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"תכונות כמו \'שיתוף מהיר\', \'איפה המכשיר שלי\' ומיקום המכשיר משתמשות בחיבור Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth יופעל מחר בשעה 5 בבוקר"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> סוללה"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"תיעוד הבעיה"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"התחלה"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"עצירה"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"דיווח על באג"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"איזה חלק בחוויית השימוש שלך במכשיר הושפע?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"בחירה בסוג הבעיה"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"הקלטת המסך"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"רגילה"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"בינונית"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"גבוהה"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"מכשירי שמיעה"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"מכשירי שמיעה"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"התאמה של מכשיר חדש"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"צריך ללחוץ כדי להתאים מכשיר חדש"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"לבטל את חסימת המיקרופון של המכשיר?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"לבטל את חסימת המצלמה של המכשיר?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"לבטל את חסימת המצלמה והמיקרופון של המכשיר?"</string>
@@ -481,10 +486,10 @@
<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>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> השביתה את האפשרות הזו"</string>
- <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"להתחיל את ההעברה?"</string>
+ <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>
- <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"התחלת ההעברה (cast)"</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"בזמן Cast מאפליקציה, תהיה ל-Android גישה לכל מה שמופיע באפליקציה ולכל מדיה שפועלת בה. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
+ <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"הפעלת Cast"</string>
<string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"להתחיל את השיתוף?"</string>
<string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"בזמן שיתוף, הקלטה או העברה (cast) תהיה ל-Android גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"בזמן שיתוף, הקלטה או העברה (cast) של אפליקציה, תהיה ל-Android גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"החלפה של פריסת מקלדת"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"או"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ניקוי שאילתת החיפוש"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"מקשי קיצור"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"מקשי קיצור"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"חיפוש מקשי קיצור"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"לא נמצאו מקשי קיצור"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"מערכת"</string>
@@ -757,7 +762,7 @@
<string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"מוצגים: קיצורי הדרך של הקלט"</string>
<string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"מוצגים: קיצורי הדרך לפתיחת אפליקציות"</string>
<string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"מוצגים: קיצורי הדרך של האפליקציה הנוכחית"</string>
- <string name="group_system_access_notification_shade" msgid="1619028907006553677">"הצגת הודעות"</string>
+ <string name="group_system_access_notification_shade" msgid="1619028907006553677">"הצגת התראות"</string>
<string name="group_system_full_screenshot" msgid="5742204844232667785">"צילום המסך"</string>
<string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"הצגת מקשי הקיצור"</string>
<string name="group_system_go_back" msgid="2730322046244918816">"חזרה"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"לפתיחת Google Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"מסך הנעילה"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"כתיבת הערה"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ריבוי משימות מערכת"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"כניסה למסך מפוצל עם האפליקציה הנוכחית ל-RHS"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"כניסה למסך מפוצל עם האפליקציה הנוכחית ל-LHS"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ריבוי משימות"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"שימוש במסך מפוצל כשהאפליקציה הנוכחית בצד ימין"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"שימוש במסך מפוצל כשהאפליקציה הנוכחית בצד שמאל"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"החלפה ממסך מפוצל למסך מלא"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"מעבר לאפליקציה משמאל או למטה בזמן שימוש במסך מפוצל"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"מעבר לאפליקציה מימין או למעלה בזמן שימוש במסך מפוצל"</string>
@@ -1247,7 +1252,7 @@
<string name="home_quick_affordance_unavailable_configure_the_app" msgid="604424593994493281">"• יש לפחות מכשיר אחד או פאנל מכשיר אחד זמינים"</string>
<string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"צריך לבחור אפליקציית פתקים שתיפתח כברירת מחדל כשייעשה שימוש במקש הקיצור לכתיבת פתקים"</string>
<string name="keyguard_affordance_enablement_dialog_notes_app_action" msgid="6821710209675089470">"בחירת אפליקציה"</string>
- <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"מקש קיצור ללחיצה ארוכה"</string>
+ <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"צריך ללחוץ לחיצה ארוכה על הלחצן"</string>
<string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ביטול"</string>
<string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"כן, אני רוצה להחליף בין המסכים"</string>
<string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"פתיחת הטלפון"</string>
@@ -1304,6 +1309,6 @@
<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>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"התאורה האחורית במקלדת"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"רמה %1$d מתוך %2$d"</string>
- <string name="home_controls_dream_label" msgid="6567105701292324257">"ממשק השליטה במכשירים"</string>
+ <string name="home_controls_dream_label" msgid="6567105701292324257">"שליטה במכשירים"</string>
<string name="home_controls_dream_description" msgid="4644150952104035789">"גישה מהירה לממשק השליטה במכשירים כשומר מסך"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
index 196a6c2..b5cb476 100644
--- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"מושבת"</item>
<item msgid="5137565285664080143">"מופעל"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"לא זמינים"</item>
+ <item msgid="3079622119444911877">"מצב מושבת"</item>
+ <item msgid="3028994095749238254">"מצב פעיל"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 6e2ca40..f403308 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -173,7 +173,7 @@
<string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PIN が正しくありません"</string>
<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>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"試行回数が上限に達しました。\n<xliff:g id="NUMBER">%d</xliff:g> 秒後にもう一度お試しください。"</string>
<string name="work_challenge_emergency_button_text" msgid="8946588434515599288">"緊急通報"</string>
<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>
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"すべて表示"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth を使用"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"接続しました"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"保存しました"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"接続を解除"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"有効化"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明日自動的に ON に戻す"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"クイック共有、デバイスを探す、デバイスの位置情報などの機能は Bluetooth を使用します"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"午前 5 時に Bluetooth が ON になります"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"バッテリー <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"オーディオ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ヘッドセット"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"録音に関する問題"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"開始"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"バグレポート"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"デバイスのどの部分が影響を受けましたか?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"問題の種類を選択する"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"スクリーン レコード"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"中"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"高"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"補聴器"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"補聴器"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"新しいデバイスとペア設定"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"クリックすると、新しいデバイスをペア設定できます"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"デバイスのマイクのブロックを解除しますか?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"デバイスのカメラのブロックを解除しますか?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"デバイスのカメラとマイクのブロックを解除しますか?"</string>
@@ -550,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"このデバイスは保護者によって管理されています。保護者は、あなたが使用するアプリ、あなたの現在地、デバイスの利用時間などの情報を確認したり、管理したりできます。"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"信頼エージェントがロック解除を管理"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"認証の試行回数が上限を超えたため、デバイスがロックされました"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"認証の試行回数が上限に達したため、デバイスがロックされました"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"デバイスがロックされました\n認証に失敗しました"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>。<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"音声の設定"</string>
@@ -607,7 +613,7 @@
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"空間オーディオ"</string>
<string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"OFF"</string>
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"固定"</string>
- <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ヘッド トラッキング"</string>
+ <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ヘッド トラッキング"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"タップすると、着信音のモードを変更できます"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ミュート"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ミュートを解除"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"キーボード レイアウトの切り替え"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"または"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"検索クエリをクリア"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ショートカット"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"キーボード ショートカット"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ショートカットの検索"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ショートカットがありません"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"システム"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"アシスタントを開く"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"画面をロック"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"メモを入力する"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"システム マルチタスク"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"分割画面にして現在のアプリを右側に設定する"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"分割画面にして現在のアプリを左側に設定する"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"マルチタスク"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"分割画面の使用(現在のアプリを右側に表示)"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"分割画面の使用(現在のアプリを左側に表示)"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"分割画面から全画面に切り替える"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"分割画面の使用時に右側または下部のアプリに切り替える"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"分割画面の使用時に左側または上部のアプリに切り替える"</string>
@@ -1304,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"キーボード バックライト"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"レベル %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ホーム コントロール"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"ホーム コントロールにスクリーンセーバーとしてすばやくアクセス"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"ホーム コントロールにスクリーンセーバーからすばやくアクセス"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index b2295f3..d9483ec 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ყველას ნახვა"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ის გამოყენება"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"დაკავშირებული"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"შენახული"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"კავშირის გაწყვეტა"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"გააქტიურება"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ხელახლა ავტომატურად ჩართვა ხვალ"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ფუნქციები, როგორებიცაა „სწრაფი გაზიარება“, „ჩემი მოწყობილობის პოვნა“ და „მოწყობილობის მდებარეობა“ იყენებს Bluetooth-ს"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ჩაირთვება ხვალ დილის 5 საათზე"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ბატარეა"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"ჩაწერასთან დაკავშირებული პრობლემა"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"დაწყება"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"გაჩერება"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"სისტემის ხარვეზის ანგარიში"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"თქვენი მოწყობილობის გამოცდილების რა ნაწილზე მოხდა ზეგავლენა?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"აირჩიეთ პრობლემის ტიპი"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ეკრანის ჩანაწერი"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"საშუალო"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"მაღალი"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"სმენის აპარატები"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"სმენის აპარატები"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ახალი მოწყობილობის დაწყვილება"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"დააწკაპუნეთ ახალი მოწყობილობის დასაწყვილებლად"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"გსურთ მოწყობილობის მიკროფონის განბლოკვა?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"გსურთ მოწყობილობის კამერის განბლოკვა?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"გსურთ მოწყობილობის კამერის და მიკროფონის განბლოკვა?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"კლავიატურის განლაგების გადართვა"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ან"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"საძიებო ფრაზის გასუფთავება"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"მალსახმობები"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"კლავიატურის მალსახმობები"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"მალსახმობების ძიება"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"მალსახმობები ვერ მოიძებნა"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"სისტემა"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"ასისტენტის გახსნა"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ჩაკეტილი ეკრანი"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ჩაინიშნეთ"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"სისტემის მრავალამოცანიანი რეჟიმი"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"ეკრანის გაყოფის შეყვანა მიმდინარე აპით RHS-ში"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"ეკრანის გაყოფის შეყვანა მიმდინარე აპით LHS-ში"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"მრავალამოცანიანი რეჟიმი"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"ეკრანის გაყოფის გამოყენება მიმდინარე აპზე მარჯვნივ"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"ეკრანის გაყოფის გამოყენება მიმდინარე აპზე მარცხნივ"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"გადართვა ეკრანის გაყოფიდან სრულ ეკრანზე"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ეკრანის გაყოფის გამოყენებისას აპზე მარჯვნივ ან ქვემოთ გადართვა"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ეკრანის გაყოფის გამოყენებისას აპზე მარცხნივ ან ზემოთ გადართვა"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 127e81e..36ae88a 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -270,18 +270,26 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Барлығын көру"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ты пайдалану"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Қосылды"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сақталды"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажырату"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"іске қосу"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ертең автоматты түрде қосылсын"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, Find My Device сияқты функциялар мен құрылғы локациясы Bluetooth пайдаланады."</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ертең таңғы сағат 5-те қосылады."</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Кіріс"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Есту аппараттары"</string>
- <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Қосылуда…"</string>
+ <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Қосылып жатыр…"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматты түрде бұру"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматты айналатын экран"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Локация"</string>
@@ -297,7 +305,7 @@
<string name="quick_settings_networks_available" msgid="1875138606855420438">"Желілер бар"</string>
<string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Желілер қолжетімді емес."</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Қолжетімді Wi-Fi желілері жоқ"</string>
- <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Қосылуда…"</string>
+ <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Қосылып жатыр…"</string>
<string name="quick_settings_cast_title" msgid="2279220930629235211">"Экранды трансляциялау"</string>
<string name="quick_settings_casting" msgid="1435880708719268055">"Трансляциялануда"</string>
<string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"Атаусыз құрылғы"</string>
@@ -312,9 +320,9 @@
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Жабу"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Қосылды"</string>
<string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"Қосылды, батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="quick_settings_connecting" msgid="2381969772953268809">"Қосылуда…"</string>
+ <string name="quick_settings_connecting" msgid="2381969772953268809">"Қосылып жатыр…"</string>
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Хотспот"</string>
- <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Қосылуда…"</string>
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Қосылып жатыр…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Трафикті үнемдеу режимі қосулы"</string>
<string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# құрылғы}other{# құрылғы}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Қолшам"</string>
@@ -331,13 +339,13 @@
<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>
- <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"Қосылу уақыты: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"Қосылатын уақыты: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> дейін"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Қараңғы режим"</string>
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Батареяны үнемдеу режимі"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Күн батқанда қосу"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Күн шыққанға дейін"</string>
- <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Қосылу уақыты: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Қосылатын уақыты: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> дейін"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_bedtime" msgid="2274300599408864897">"Ұйқы режимінде"</string>
<string name="quick_settings_dark_mode_secondary_label_until_bedtime_ends" msgid="1790772410777123685">"Ұйқы режимі аяқталғанға дейін"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Ақауды жазу"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Бастау"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Тоқтату"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Қате туралы есеп"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Құрылғы қызметінің қандай түріне әсер етті?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Мәселе түрін таңдаңыз."</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экранды жазу"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Орташа"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Жоғары"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Есту құрылғылары"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Есту құрылғылары"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңа құрылғыны жұптау"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңа құрылғыны жұптау үшін басыңыз."</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Құрылғы микрофонын блоктан шығару керек пе?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Құрылғы камерасын блоктан шығару керек пе?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Құрылғы камерасы мен микрофонын блоктан шығару керек пе?"</string>
@@ -605,7 +611,7 @@
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Дыбысын өшіру үшін түртіңіз."</string>
<string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Шуды реттеу"</string>
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Кеңістіктік дыбыс"</string>
- <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Өшіру"</string>
+ <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Өшірілген"</string>
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Бекітілген"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Бас қимылын қадағалау"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Қоңырау режимін өзгерту үшін түртіңіз."</string>
@@ -636,7 +642,7 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Пайдалану үшін құлыпты ашу"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Карталарыңыз алынбады, кейінірек қайталап көріңіз."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Экран құлпының параметрлері"</string>
- <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR кодының сканері"</string>
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR сканері"</string>
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Жаңартылып жатыр"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жұмыс профилі"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ұшақ режимі"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Пернетақта форматын ауыстыру"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"немесе"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Іздеу сұрауын өшіру"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Перне тіркесімдері"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Перне тіркесімдері"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Перне тіркесімдерін іздеу"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Перне тіркесімдері табылмады."</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Жүйе"</string>
@@ -762,19 +768,19 @@
<string name="group_system_go_back" msgid="2730322046244918816">"Артқа"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"Негізгі экранға өту"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"Соңғы қолданбаларды көру"</string>
- <string name="group_system_cycle_forward" msgid="5478663965957647805">"Соңғы қолданбаларға алға өту"</string>
- <string name="group_system_cycle_back" msgid="8194102916946802902">"Соңғы қолданбаларға артқа өту"</string>
+ <string name="group_system_cycle_forward" msgid="5478663965957647805">"Соңғы қолданбалар тізімінде алға өту"</string>
+ <string name="group_system_cycle_back" msgid="8194102916946802902">"Соңғы қолданбалар тізімінде артқа өту"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Қолданбалар тізімін ашу"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Параметрлерді ашу"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant-ті ашу"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Экранды құлыптау"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Ескертпе жазу"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Жүйе мультитаскингі"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Бөлінген экран режиміне кіру (ағымдағы қолданбаны оңға орналастыру)"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Бөлінген экран режиміне кіру (ағымдағы қолданбаны солға орналастыру)"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Мультитаскинг"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Ағымдағы қолданба оң жақта тұратын бөлінген экранды пайдаланыңыз"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Ағымдағы қолданба сол жақта тұратын бөлінген экранды пайдаланыңыз"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Бөлінген экран режимінен толық экран режиміне ауысу"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Экранды бөлуді қолданғанда, оң не жоғары жақтағы қолданбаға ауысыңыз"</string>
- <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Экранды бөлуді қолданғанда, сол не жоғары жақтағы қолданбаға ауысыңыз"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлінген экранда оң не төмен жақтағы қолданбаға ауысу"</string>
+ <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлінген экранда сол не жоғары жақтағы қолданбаға ауысу"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Экранды бөлу кезінде: бір қолданбаны басқасымен алмастыру"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Енгізу"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Келесі тілге ауысу"</string>
@@ -1261,7 +1267,7 @@
<string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Қалған батарея заряды: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусты зарядтағышқа жалғаңыз."</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Стилус батареясының заряды аз"</string>
- <string name="video_camera" msgid="7654002575156149298">"Бейнекамера"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Бейнекамера"</string>
<string name="call_from_work_profile_title" msgid="5418253516453177114">"Жеке қолданбадан қоңырау шалу мүмкін емес"</string>
<string name="call_from_work_profile_text" msgid="2856337395968118274">"Ұйымыңыз тек жұмыс қолданбаларынан қоңырау шалуға рұқсат етеді."</string>
<string name="call_from_work_profile_action" msgid="2937701298133010724">"Жұмыс профиліне ауысу"</string>
@@ -1284,7 +1290,7 @@
<string name="dismiss_dialog" msgid="2195508495854675882">"Жабу"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Дисплей қосылды"</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>
@@ -1304,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Пернетақта жарығы"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Деңгей: %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Үй басқару элементтері"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Үй басқару элементтерін скринсейвер ретінде жылдам қолдану"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Үй басқару элементтерін скринсейверден қолдану"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index f2596db..c7868db 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"មើលទាំងអស់"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ប្រើប៊្លូធូស"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"បានភ្ជាប់"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"បានរក្សាទុក"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ផ្ដាច់"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"បើកដំណើរការ"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"បើកដោយស្វ័យប្រវត្តិម្ដងទៀតនៅថ្ងៃស្អែក"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"មុខងារដូចជា Quick Share, រកឧបករណ៍របស់ខ្ញុំ និងប៊្លូធូសប្រើប្រាស់ទីតាំងឧបករណ៍"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ប៊្លូធូសនឹងបើកនៅថ្ងៃស្អែកនៅម៉ោង 5 ព្រឹក"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"កត់ត្រាបញ្ហា"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"ចាប់ផ្ដើម"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"បញ្ឈប់"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"របាយការណ៍អំពីបញ្ហា"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"តើផ្នែកអ្វីនៃបទពិសោធប្រើប្រាស់ឧបករណ៍របស់អ្នកបានរងការប៉ះពាល់?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ជ្រើសរើសប្រភេទបញ្ហា"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ការថតវីដេអូអេក្រង់"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"មធ្យម"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"ខ្ពស់"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ឧបករណ៍ជំនួយការស្ដាប់"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ឧបករណ៍ជំនួយការស្ដាប់"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ផ្គូផ្គងឧបករណ៍ថ្មី"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ចុច ដើម្បីផ្គូផ្គងឧបករណ៍ថ្មី"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ឈប់ទប់ស្កាត់មីក្រូហ្វូនរបស់ឧបករណ៍ឬ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ឈប់ទប់ស្កាត់កាមេរ៉ារបស់ឧបករណ៍ឬ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ឈប់ទប់ស្កាត់កាមេរ៉ា និងមីក្រូហ្វូនរបស់ឧបករណ៍ឬ?"</string>
@@ -607,7 +613,7 @@
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"សំឡេងលំហ"</string>
<string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"បិទ"</string>
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ថេរ"</string>
- <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ការតាមដានក្បាល"</string>
+ <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"រេតាមក្បាល"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ចុចដើម្បីប្ដូរមុខងាររោទ៍"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"បិទសំឡេង"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"បើកសំឡេង"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"ប្ដូរប្លង់ក្ដារចុច"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ឬ"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"សម្អាតសំណួរស្វែងរក"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ផ្លូវកាត់"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"ផ្លូវកាត់ក្ដារចុច"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ស្វែងរកផ្លូវកាត់"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"រកផ្លូវកាត់មិនឃើញទេ"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"ប្រព័ន្ធ"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"បើកជំនួយការ"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ចាក់សោអេក្រង់"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"កត់ចំណាំ"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ការដំណើរការបានច្រើននៃប្រព័ន្ធ"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"ចូលក្នុងមុខងារបំបែកអេក្រង់ដោយប្រើកម្មវិធីបច្ចុប្បន្ននៅខាងស្ដាំដៃ"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"ចូលក្នុងមុខងារបំបែកអេក្រង់ដោយប្រើកម្មវិធីបច្ចុប្បន្ននៅខាងឆ្វេងដៃ"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ការដំណើរការបានច្រើន"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"ប្រើមុខងារបំបែកអេក្រង់ជាមួយកម្មវិធីបច្ចុប្បន្ននៅខាងស្ដាំ"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"ប្រើមុខងារបំបែកអេក្រង់ជាមួយកម្មវិធីបច្ចុប្បន្ននៅខាងឆ្វេង"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"ប្ដូរពីមុខងារបំបែកអេក្រង់ទៅជាអេក្រង់ពេញ"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ប្ដូរទៅកម្មវិធីនៅខាងស្ដាំ ឬខាងក្រោម ពេលកំពុងប្រើមុខងារបំបែកអេក្រង់"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ប្ដូរទៅកម្មវិធីនៅខាងឆ្វេង ឬខាងលើ ពេលកំពុងប្រើមុខងារបំបែកអេក្រង់"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 7323450..42d655e 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -237,7 +237,7 @@
<string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಅಧಿಸೂಚನೆಯ ಪರದೆ."</string>
<string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ಲಾಕ್ ಸ್ಕ್ರೀನ್."</string>
<string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ಕೆಲಸದ ಲಾಕ್ ಪರದೆ"</string>
- <string name="accessibility_desc_close" msgid="8293708213442107755">"ಮುಚ್ಚು"</string>
+ <string name="accessibility_desc_close" msgid="8293708213442107755">"ಮುಚ್ಚಿ"</string>
<string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ಸಂಪೂರ್ಣ ನಿಶ್ಯಬ್ಧ"</string>
<string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"ಅಲಾರಮ್ಗಳು ಮಾತ್ರ"</string>
<string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ."</string>
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ಬ್ಲೂಟೂತ್ ಬಳಸಿ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ಡಿಸ್ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ನಾಳೆ ಪುನಃ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಮಾಡಿ"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ಕ್ವಿಕ್ ಶೇರ್, Find My Device ನಂತಹ ಫೀಚರ್ಗಳು ಹಾಗೂ ಸಾಧನದ ಸ್ಥಳವು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಬಳಸುತ್ತವೆ"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಗ್ಗೆ 5 ಗಂಟೆಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್ಸೆಟ್"</string>
@@ -329,7 +337,7 @@
<string name="quick_settings_work_mode_label" msgid="6440531507319809121">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ಗಳು"</string>
<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_on_at_sunset" msgid="3358706312129866626">"ಸೂರ್ಯಾಸ್ತಕ್ಕೆ ಆನ್"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ಸೂರ್ಯೋದಯದವರೆಗೆ"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"<xliff:g id="TIME">%s</xliff:g> ಸಮಯದಲ್ಲಿ"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> ವರೆಗೂ"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"ರೆಕಾರ್ಡ್ ದೋಷ"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"ಪ್ರಾರಂಭಿಸಿ"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"ನಿಲ್ಲಿಸಿ"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ಬಗ್ ವರದಿ ಮಾಡುವಿಕೆ"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ಸಾಧನ ಬಳಸುವಾಗ ನೀವು ಯಾವ ರೀತಿಯ ಸಮಸ್ಯೆ ಎದುರಿಸುತ್ತೀರಿ?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ಸಮಸ್ಯೆಯ ಪ್ರಕಾರವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ಪ್ರಮಾಣಿತ"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"ಮಧ್ಯಮ"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"ಹೆಚ್ಚು"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ಹಿಯರಿಂಗ್ ಸಾಧನಗಳು"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ಹಿಯರಿಂಗ್ ಸಾಧನಗಳು"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ಹೊಸ ಸಾಧನವನ್ನು ಪೇರ್ ಮಾಡಿ"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ಸಾಧನದ ಕ್ಯಾಮರಾ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ಸಾಧನದ ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೋಫೋನ್ ಅನ್ನು ಅನ್ಬ್ಲಾಕ್ ಮಾಡಬೇಕೇ?"</string>
@@ -551,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ಈ ಸಾಧನವನ್ನು ನಿಮ್ಮ ಪೋಷಕರು ನಿರ್ವಹಿಸುತ್ತಿದ್ದಾರೆ. ನೀವು ಬಳಸುವ ಆ್ಯಪ್ಗಳು, ನಿಮ್ಮ ಸ್ಥಳ ಮತ್ತು ನಿಮ್ಮ ವೀಕ್ಷಣಾ ಅವಧಿಯಂತಹ ಮಾಹಿತಿಯನ್ನು ನಿಮ್ಮ ಪೋಷಕರು ನೋಡಬಹುದು ಮತ್ತು ನಿರ್ವಹಿಸಬಹುದು."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ನಿಂದ ಅನ್ಲಾಕ್ ಮಾಡಲಾಗಿದೆ"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ಸಾಧನವನ್ನು ಲಾಕ್ ಮಾಡಲಾಗಿದೆ, ಹಲವಾರು ದೃಢೀಕರಣ ಪ್ರಯತ್ನಗಳು"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ಹಲವಾರು ದೃಢೀಕರಣ ಪ್ರಯತ್ನಗಳನ್ನು ಮಾಡಿರುವ ಕಾರಣ ಸಾಧನವನ್ನು ಲಾಕ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"ಸಾಧನವನ್ನು ಲಾಕ್ ಮಾಡಲಾಗಿದೆ\nದೃಢೀಕರಣ ವಿಫಲವಾಗಿದೆ"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ಸೌಂಡ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"ಕೀಬೋರ್ಡ್ ಲೇಔಟ್ ಬದಲಾಯಿಸಿ"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ಅಥವಾ"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ಹುಡುಕಾಟದ ಪ್ರಶ್ನೆಯನ್ನು ತೆರವುಗೊಳಿಸಿ"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ಶಾರ್ಟ್ಕಟ್ಗಳು"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಹುಡುಕಿ"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ಯಾವುದೇ ಶಾರ್ಟ್ಕಟ್ಗಳಿಲ್ಲ"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"ಸಿಸ್ಟಂ"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"assistant ಅನ್ನು ತೆರೆಯಿರಿ"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಮಾಡಿ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ಟಿಪ್ಪಣಿಯನ್ನು ತೆಗೆದುಕೊಳ್ಳಿ"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ಸಿಸ್ಟಂ ಮಲ್ಟಿಟಾಸ್ಕಿಂಗ್"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"RHS ಗೆ ಇರುವ ಪ್ರಸ್ತುತ ಆ್ಯಪ್ ಸಹಾಯದಿಂದ ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ನಮೂದಿಸಿ"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"LHS ಗೆ ಇರುವ ಪ್ರಸ್ತುತ ಆ್ಯಪ್ ಸಹಾಯದಿಂದ ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ನಮೂದಿಸಿ"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ಮಲ್ಟಿಟಾಸ್ಕಿಂಗ್"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"ಬಲಭಾಗದಲ್ಲಿ ಪ್ರಸ್ತುತ ಆ್ಯಪ್ ಮೂಲಕ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಬಳಸಿ"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"ಎಡಭಾಗದಲ್ಲಿ ಪ್ರಸ್ತುತ ಆ್ಯಪ್ ಮೂಲಕ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಬಳಸಿ"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ನಿಂದ ಪೂರ್ಣ ಸ್ಕ್ರೀನ್ಗೆ ಬದಲಿಸಿ"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ಪರದೆ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ಬಳಸುವಾಗ ಬಲಭಾಗ ಅಥವಾ ಕೆಳಭಾಗದಲ್ಲಿರುವ ಆ್ಯಪ್ಗೆ ಬದಲಿಸಿ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ಪರದೆ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ಬಳಸುವಾಗ ಎಡಭಾಗ ಅಥವಾ ಮೇಲ್ಭಾಗದಲ್ಲಿರುವ ಆ್ಯಪ್ಗೆ ಬದಲಿಸಿ"</string>
@@ -1304,6 +1309,6 @@
<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>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ಕೀಬೋರ್ಡ್ ಬ್ಯಾಕ್ಲೈಟ್"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ರಲ್ಲಿ %1$d ಮಟ್ಟ"</string>
- <string name="home_controls_dream_label" msgid="6567105701292324257">"ಹೋಮ್ ನಿಯಂತ್ರಣಗಳು"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"ಹೋಮ್ ನಿಯಂತ್ರಣವನ್ನು ಸ್ಕ್ರೀನ್ಸೇವರ್ನಂತೆ ತ್ವರಿತವಾಗಿ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ"</string>
+ <string name="home_controls_dream_label" msgid="6567105701292324257">"ಮನೆ ನಿಯಂತ್ರಣಗಳು"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"ಮನೆ ನಿಯಂತ್ರಣವನ್ನು ಸ್ಕ್ರೀನ್ಸೇವರ್ನಂತೆ ಬೇಗ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index 9628323..afc7c1a 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"ಆಫ್ ಆಗಿದೆ"</item>
<item msgid="5137565285664080143">"ಆನ್ ಆಗಿದೆ"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"ಲಭ್ಯವಿಲ್ಲ"</item>
+ <item msgid="3079622119444911877">"ಆಫ್ ಆಗಿದೆ"</item>
+ <item msgid="3028994095749238254">"ಆನ್"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 9a4c60b..3c34f8d 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"모두 보기"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"블루투스 사용"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"연결됨"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"저장됨"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"연결 해제"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"실행"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"내일 다시 자동으로 사용 설정"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, 내 기기 찾기, 기기 위치 등의 기능에서 블루투스를 사용합니다."</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"블루투스가 내일 오전 5시에 켜집니다."</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"문제 기록"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"시작"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"중지"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"버그 신고"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"기기 경험의 어떤 부분에 영향이 있었나요?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"문제 유형 선택"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"화면 녹화"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"표준"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"보통"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"높음"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"청각 보조 기기"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"청각 보조 기기"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"새 기기와 페어링"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"새 기기와 페어링하려면 클릭하세요"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"기기 마이크를 차단 해제하시겠습니까?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"기기 카메라를 차단 해제하시겠습니까?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"기기 카메라 및 마이크를 차단 해제하시겠습니까?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"키보드 레이아웃 전환"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"또는"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"검색어 삭제"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"단축키"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"단축키"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"단축키 검색"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"단축키 없음"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"시스템"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"어시스턴트 열기"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"잠금 화면"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"메모 작성"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"시스템 멀티태스킹"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"현재 앱을 오른쪽으로 보내는 화면 분할 입력"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"현재 앱을 왼쪽으로 보내는 화면 분할 입력"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"멀티태스킹"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"현재 앱이 오른쪽에 오도록 화면 분할 사용"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"현재 앱이 왼쪽에 오도록 화면 분할 사용"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"화면 분할에서 전체 화면으로 전환"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"화면 분할을 사용하는 중에 오른쪽 또는 아래쪽에 있는 앱으로 전환하기"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"화면 분할을 사용하는 중에 왼쪽 또는 위쪽에 있는 앱으로 전환하기"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"키보드 백라이트"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d단계 중 %1$d단계"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"홈 컨트롤"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"화면 보호기로 홈 컨트롤에 빠르게 액세스합니다."</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"화면 보호기로 홈 컨트롤에 빠르게 액세스하기"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
index d30aff2..bc4740d 100644
--- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"사용 안함"</item>
<item msgid="5137565285664080143">"사용"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"사용 불가"</item>
+ <item msgid="3079622119444911877">"사용 안함"</item>
+ <item msgid="3028994095749238254">"사용"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index d893887..ad9d2f7 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -212,7 +212,7 @@
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"Bluetooth түзмөгүнүн сүрөтчөсү"</string>
<string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Түзмөктүн чоо-жайын конфигурациялоо үчүн чыкылдатыңыз"</string>
<string name="accessibility_bluetooth_device_settings_see_all" msgid="9111952496905423543">"Бардык түзмөктөрдү көрүү үчүн чыкылдатыңыз"</string>
- <string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="2435184865793496966">"Жаңы түзмөктү жупташтыруу үчүн чыкылдатыңыз"</string>
+ <string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="2435184865793496966">"Жаңы түзмөк кошуу үчүн чыкылдатыңыз"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Батарея кубатынын деңгээли белгисиз."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> менен туташкан."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> менен туташты."</string>
@@ -266,16 +266,24 @@
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Жупташкан түзмөктөр жок"</string>
<string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Түзмөктү туташтыруу же ажыратуу үчүн таптаңыз"</string>
- <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Жаңы түзмөктү жупташтыруу"</string>
+ <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Жаңы түзмөк кошуу"</string>
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Баарын көрүү"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Иштетүү"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Туташты"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сакталды"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажыратуу"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"иштетүү"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Эртең автоматтык түрдө кайра күйгүзүү"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Тез Бөлүшүү, \"Түзмөгүм кайда?\" жана түзмөктүн турган жерин аныктоо сыяктуу функциялар Bluetooth\'ду колдонот"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth эртең саат 05:00 күйөт"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Маселени жаздыруу"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Баштоо"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Токтотуу"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Мүчүлүштүк тууралуу кабарлоо"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Түзмөгүңүздүн кайсы бөлүгүнө кедергиси тийди?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Маселенин түрүн тандоо"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экрандан видео жаздырып алуу"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Кадимки"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Орточо"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Жогору"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Угуу аппараттары"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Угуу аппараттары"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңы түзмөк кошуу"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңы түзмөк кошуу үчүн басыңыз"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Түзмөктүн микрофонун бөгөттөн чыгарасызбы?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Түзмөктүн камерасын бөгөттөн чыгарасызбы?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Түзмөктүн камерасы менен микрофону бөгөттөн чыгарылсынбы?"</string>
@@ -551,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Бул түзмөктү ата-энең башкарат. Ата-энең сен иштеткен колдонмолорду, кайда жүргөнүңдү жана түзмөктү канча убакыт колдонгонуңду көрүп, башкарып турат."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ишеним агенти кулпусун ачты"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Түзмөк кулпуланды. Аутентификациядан өтүүгө өтө көп аракет жасалды"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Аутентификациядан өтө албай койгонуңуздан улам, түзмөк кулпуланды"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Түзмөк кулпуланды\nАутентификациядан өткөн жоксуз"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Добуштун параметрлери"</string>
@@ -719,7 +724,7 @@
<string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Ортолотуу"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Боштук"</string>
- <string name="keyboard_key_enter" msgid="8633362970109751646">"Киргизүү"</string>
+ <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Артка өчүрүү"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Ойнотуу/Тындыруу"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"Токтотуу"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Баскычтоп калыбын которуштуруу"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"же"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Изделген суроону тазалоо"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Ыкчам баскычтар"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Ыкчам баскычтар"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Ыкчам баскычтарды издөө"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ыкчам баскычтар жок"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Система"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Жардамчыны ачуу"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Экранды кулпулоо"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Кыска жазуу түзүү"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Системанын бир нече тапшырма аткаруусу"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Учурда оң жактагы колдонмо менен экранды бөлүүнү иштетүү"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Учурда сол жактагы колдонмо менен экранды бөлүүнү иштетүү"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Бир нече тапшырма аткаруу"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Учурдагы колдонмону оңго жылдырып, экранды бөлүү"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Учурдагы колдонмону солго жылдырып, экранды бөлүү"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Экранды бөлүү режиминен толук экранга которулуу"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлүнгөн экранды колдонуп жатканда сол же төмөн жактагы колдонмого которулуңуз"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлүнгөн экранды колдонуп жатканда сол же жогору жактагы колдонмого которулуңуз"</string>
@@ -1286,21 +1291,21 @@
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Экран туташтырылды"</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>
<string name="privacy_dialog_close_app_button" msgid="8006250171305878606">"Бул колдонмону жабуу"</string>
<string name="privacy_dialog_close_app_message" msgid="1316408652526310985">"<xliff:g id="APP_NAME">%1$s</xliff:g> жабылды"</string>
<string name="privacy_dialog_manage_service" msgid="8320590856621823604">"Кызматты тескөө"</string>
- <string name="privacy_dialog_manage_permissions" msgid="2543451567190470413">"Кирүү мүмкүнчүлүгүн тескөө"</string>
+ <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_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>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Баскычтоптун жарыгы"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ичинен %1$d-деңгээл"</string>
diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
index 35795b7..694967e 100644
--- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Өчүк"</item>
<item msgid="5137565285664080143">"Күйүк"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Жеткиликсиз"</item>
+ <item msgid="3079622119444911877">"Өчүк"</item>
+ <item msgid="3028994095749238254">"Күйүк"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 873d75e..eb9e1eb 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ເບິ່ງທັງໝົດ"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ໃຊ້ Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ບັນທຶກແລ້ວ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ຕັດການເຊື່ອມຕໍ່"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ເປີດນຳໃຊ້"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ເປີດໃຊ້ໂດຍອັດຕະໂນມັດອີກຄັ້ງມື້ອື່ນ"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ຄຸນສົມບັດຕ່າງໆ ເຊັ່ນ: ການແຊຣ໌ດ່ວນ, ຊອກຫາອຸປະກອນຂອງຂ້ອຍ ແລະ ສະຖານທີ່ຂອງອຸປະກອນແມ່ນໃຊ້ Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ຈະເປີດມື້ອື່ນເວລາ 05:00 ໂມງ"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ສຽງ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ຊຸດຫູຟັງ"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"ບັນຫາກ່ຽວກັບການບັນທຶກ"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"ເລີ່ມ"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"ຢຸດ"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ລາຍງານຂໍ້ຜິດພາດ"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ສ່ວນໃດຂອງປະສົບການອຸປະກອນຂອງທ່ານໄດ້ຮັບຜົນກະທົບ?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ເລືອກປະເພດບັນຫາ"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ບັນທຶກໜ້າຈໍ"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"ປານກາງ"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"ສູງ"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ອຸປະກອນຊ່ວຍຟັງ"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ອຸປະກອນຊ່ວຍຟັງ"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ຄລິກເພື່ອຈັບຄູ່ອຸປະກອນໃໝ່"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ປົດບລັອກໄມໂຄຣໂຟນອຸປະກອນບໍ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ປົດບລັອກກ້ອງຖ່າຍຮູບອຸປະກອນບໍ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ຍົກເລີກການບລັອກກ້ອງຖ່າຍຮູບ ຫຼື ໄມໂຄຣໂຟນອຸປະກອນບໍ?"</string>
@@ -652,7 +658,7 @@
<string name="tuner_warning_title" msgid="7721976098452135267">"ມ່ວນຊື່ນສຳລັບບາງຄົນ ແຕ່ບໍ່ແມ່ນສຳລັບທຸກຄົນ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner ໃຫ້ທ່ານມີວິທີພິເສດຕື່ມອີກໃນການປັບປ່ຽນ ແລະຕົບແຕ່ງສ່ວນຕໍ່ປະສານຜູ້ໃຊ້ຂອງ Android. ຄຸນສົມບັດທົດລອງໃຊ້ເຫຼົ່ານີ້ອາດຈະປ່ຽນແປງ, ຢຸດເຊົາ ຫຼືຫາຍໄປໃນການວາງຈຳໜ່າຍໃນອະນາຄົດ. ຈົ່ງດຳເນີນຕໍ່ດ້ວຍຄວາມລະມັດລະວັງ."</string>
<string name="tuner_persistent_warning" msgid="230466285569307806">"ຄຸນສົມບັດທົດລອງໃຊ້ງານເຫຼົ່ານີ້ອາດຈະປ່ຽນແປງ, ຢຸດເຊົາ ຫຼືຫາຍໄປໃນການອອກຈຳໜ່າຍໃນອະນາຄົດ. ຈົ່ງສືບຕໍ່ດ້ວຍຄວາມລະມັດລະວັງ."</string>
- <string name="got_it" msgid="477119182261892069">"ໄດ້ແລ້ວ"</string>
+ <string name="got_it" msgid="477119182261892069">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="tuner_toast" msgid="3812684836514766951">"ຍິນດີດ້ວຍ! System UI Tuner ໄດ້ຖືກເພີ່ມໃສ່ການຕັ້ງຄ່າແລ້ວ"</string>
<string name="remove_from_settings" msgid="633775561782209994">"ເອົາອອກຈາກການຕັ້ງຄ່າ"</string>
<string name="remove_from_settings_prompt" msgid="551565437265615426">"ເອົາ System UI Tuner ອອກຈາກການຕັ້ງຄ່າ ແລະຢຸດການໃຊ້ທຸກຄຸນສົມບັດໃຊ້ງານຂອງມັນ?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"ສະຫຼັບແປ້ນພິມ"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ຫຼື"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ລຶບລ້າງຄຳຊອກຫາ"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ທາງລັດ"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"ຄີລັດ"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ທາງລັດການຊອກຫາ"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ບໍ່ພົບທາງລັດ"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"ລະບົບ"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"ເປີດຜູ້ຊ່ວຍ"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ໜ້າຈໍລັອກ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ຈົດບັນທຶກ"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ການເຮັດຫຼາຍໜ້າວຽກພ້ອມກັນຂອງລະບົບ"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"ເຂົ້າສູ່ແບ່ງໜ້າຈໍດ້ວຍແອັບປັດຈຸບັນໄປຫາ RHS"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"ເຂົ້າສູ່ແບ່ງໜ້າຈໍດ້ວຍແອັບປັດຈຸບັນໄປຫາ LHS"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ການເຮັດຫຼາຍໜ້າວຽກພ້ອມກັນ"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"ໃຊ້ການແບ່ງໜ້າຈໍກັບແອັບປັດຈຸບັນຢູ່ເບື້ອງຂວາ"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"ໃຊ້ການແບ່ງໜ້າຈໍກັບແອັບປັດຈຸບັນຢູ່ເບື້ອງຊ້າຍ"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"ສະຫຼັບຈາກແບ່ງໜ້າຈໍໄປເປັນເຕັມຈໍ"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ສະຫຼັບໄປໃຊ້ແອັບຢູ່ຂວາ ຫຼື ທາງລຸ່ມໃນຂະນະທີ່ໃຊ້ແບ່ງໜ້າຈໍ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ສະຫຼັບໄປໃຊ້ແອັບຢູ່ຊ້າຍ ຫຼື ທາງເທິງໃນຂະນະທີ່ໃຊ້ແບ່ງໜ້າຈໍ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index d5d7929..63971ab 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Žiūrėti viską"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"„Bluetooth“ naudojimas"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Prisijungta"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Išsaugota"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atjungti"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"suaktyvinti"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatiškai vėl įjungti rytoj"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Tokioms funkcijoms kaip „Spartusis bendrinimas“, „Rasti įrenginį“ ir įrenginio vietovė naudojamas „Bluetooth“ ryšys"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"„Bluetooth“ bus įjungtas rytoj, 5 val."</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumuliatorius: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Garsas"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Virtualiosios realybės įrenginys"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Įrašyti problemą"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Pradėti"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stabdyti"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Pranešimas apie riktą"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuri įrenginio funkcija buvo paveikta?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pasirinkite problemos tipą"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrano įrašas"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Įprastas"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Vidutinis"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Aukštas"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Klausos įrenginiai"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Klausos įrenginiai"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Susieti naują įrenginį"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Spustelėkite, kad susietumėte naują įrenginį"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Panaikinti įrenginio mikrofono blokavimą?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Panaikinti įrenginio fotoaparato blokavimą?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Panaikinti įrenginio fotoaparato ir mikrofono blokavimą?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Perjungti klaviat. išdėstymą"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"arba"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Išvalyti paieškos užklausą"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Spartieji klavišai"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Spartieji klavišai"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Ieškoti sparčiųjų klavišų"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Sparčiųjų klavišų nerasta"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string>
@@ -770,16 +775,16 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Atidaryti Padėjėją"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Užrakinti ekraną"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Sukurti pastabą"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Kelių užduočių atlikimas sistemoje"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Eiti į išskaidyto ekrano režimą su dabartine programa dešinėje"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Eiti į išskaidyto ekrano režimą su dabartine programa kairėje"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Kelių užduočių atlikimas"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Naudoti išskaidyto ekrano režimą su dabartine programa dešinėje"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Naudoti išskaidyto ekrano režimą su dabartine programa kairėje"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Perjungti iš išskaidyto ekrano režimo į viso ekrano režimą"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Perjunkite į programą dešinėje arba apačioje išskaidyto ekrano režimu"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Perjunkite į programą kairėje arba viršuje išskaidyto ekrano režimu"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Išskaidyto ekrano režimu: pakeisti iš vienos programos į kitą"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Įvestis"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Perjungti į kitą kalbą"</string>
- <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Perjungti ankstesnę kalbą"</string>
+ <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Perjungti į ankstesnę kalbą"</string>
<string name="input_access_emoji" msgid="8105642858900406351">"Pasiekti jaustuką"</string>
<string name="input_access_voice_typing" msgid="7291201476395326141">"Pasiekti rašymą balsu"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Programos"</string>
@@ -1297,7 +1302,7 @@
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"Naudotojo telefono skambučio programa"</string>
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"Neseniai naudojo telefono skambučio programa"</string>
<string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Naudoja <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Neseniai naudojo „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
+ <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Neseniai naudojo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Naudoja <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">"Neseniai naudojo „<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">"Naudoja <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-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
index cfa5552..c975e7e 100644
--- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Išjungta"</item>
<item msgid="5137565285664080143">"Įjungta"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Nepasiekiama"</item>
+ <item msgid="3079622119444911877">"Išjungta"</item>
+ <item msgid="3028994095749238254">"Įjungta"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 4413780..4b7507d 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Skatīt visas"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Izmantot Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Savienojums izveidots"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saglabāta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atvienot"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizēt"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automātiski atkal ieslēgt rīt"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Tādas funkcijas kā “Ātrā kopīgošana”, “Atrast ierīci” un ierīces atrašanās vietas noteikšana izmanto tehnoloģiju Bluetooth."</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth tiks ieslēgts rīt plkst. 5:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumulators: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Problēmas ierakstīšana"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Sākt"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Apturēt"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Kļūdas pārskats"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuras ierīces funkcijas tika ietekmētas?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Atlasiet problēmas veidu"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrāna ierakstīšana"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standarta"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Vidējs"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Augsts"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dzirdes aparāti"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dzirdes aparāti"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Savienojiet pārī jaunu ierīci"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Noklikšķiniet, lai savienotu pārī jaunu ierīci"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vai atbloķēt ierīces mikrofonu?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vai vēlaties atbloķēt ierīces kameru?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vai atbloķēt ierīces kameru un mikrofonu?"</string>
@@ -606,7 +611,7 @@
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Pieskarieties, lai izslēgtu skaņu."</string>
<string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Trokšņu kontrole"</string>
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Telpiskais audio"</string>
- <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Izslēgts"</string>
+ <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Izslēgta"</string>
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksēts"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seko galvai"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Pieskarieties, lai mainītu zvanītāja režīmu."</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Mainīt tastatūras izkārtojumu"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"vai"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Notīrīt meklēšanas vaicājumu"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Īsinājumtaustiņi"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Īsinājumtaustiņi"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Meklēt īsinājumtaustiņus"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nav atrasti"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistēma"</string>
@@ -769,10 +774,10 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Atvērt iestatījumus"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Atvērt Asistentu"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Bloķēt ekrānu"</string>
- <string name="group_system_quick_memo" msgid="3764560265935722903">"Piezīmes izveide"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Sistēmas vairākuzdevumu režīms"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Pāriet ekrāna sadalīšanas režīmā ar pašreizējo lietotni pa labi"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Pāriet ekrāna sadalīšanas režīmā ar pašreizējo lietotni pa kreisi"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Izveidot piezīmi"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Vairākuzdevumu režīms"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Izmantot ekrāna sadalīšanu ar pašreizējo lietotni labajā pusē"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Izmantot ekrāna sadalīšanu ar pašreizējo lietotni kreisajā pusē"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Pārslēgties no ekrāna sadalīšanas režīma uz pilnekrāna režīmu"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pāriet uz lietotni pa labi/lejā, kamēr izmantojat sadalīto ekrānu."</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pāriet uz lietotni pa kreisi/augšā, kamēr izmantojat sadalīto ekrānu."</string>
diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
index e6b4dea..c65a1d4 100644
--- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Izslēgts"</item>
<item msgid="5137565285664080143">"Ieslēgts"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Nav pieejams"</item>
+ <item msgid="3079622119444911877">"Izslēgts"</item>
+ <item msgid="3028994095749238254">"Ieslēgts"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 4f5bf6c..ba29ee6 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -270,13 +270,21 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Прикажи ги сите"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Користи Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Поврзано"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Зачувано"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекини врска"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирај"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматски вклучи повторно утре"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Функциите како „Брзо споделување“, „Најди го мојот уред“ и локација на уредот користат Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ќе се вклучи утре во 5:00"</string>
- <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батерија"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
+ <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Влез"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Евидентирајте проблем"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Започнете"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Сопрете"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Извештај за грешка"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Кој дел од доживувањето на уредот беше засегнат?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изберете тип проблем"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Снимање екран"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандарден"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Среден"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Висок"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спари нов уред"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за да спарите нов уред"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се одблокира пристапот до микрофонот на уредот?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се одблокира пристапот до камерата на уредот?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се одблокира пристапот до камерата и микрофонот на уредот?"</string>
@@ -669,7 +674,7 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматски"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибрации"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува подолу во делот со разговори"</string>
- <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или вибрира во зависност од поставките на уредот"</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или вибрира во зависност од поставките за уредот"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да ѕвони или вибрира во зависност од поставките на уредот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволете системот да определи дали известувањево треба да испушти звук или да вибрира"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Статус:</b> поставено на „Стандардно“"</string>
@@ -719,7 +724,7 @@
<string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Центар"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
- <string name="keyboard_key_enter" msgid="8633362970109751646">"Внеси"</string>
+ <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Бришење наназад"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Пушти/Паузирај"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"Сопри"</string>
@@ -745,13 +750,13 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Промени јазик на тастатура"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"или"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Бришење поим за пребарување"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Кратенки"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Кратенки од тастатура"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Пребарувајте кратенки"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Не се пронајдени кратенки"</string>
- <string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Системски"</string>
+ <string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Систем"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Внесување"</string>
- <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Отворени аплик."</string>
- <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Тековна аплик."</string>
+ <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Отворање апликации"</string>
+ <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Тековна апликација"</string>
<string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Се прикажуваат резултати од пребарувањето"</string>
<string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Се прикажуваат системски кратенки"</string>
<string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Се прикажуваат кратенки за внесување"</string>
@@ -768,14 +773,14 @@
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Отворете го списокот со апликации"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Отворете „Поставки“"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Отворете го „Помошникот“"</string>
- <string name="group_system_lock_screen" msgid="7391191300363416543">"Заклучен екран"</string>
+ <string name="group_system_lock_screen" msgid="7391191300363416543">"Заклучете го екранот"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Фатете белешка"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Системски мултитаскинг"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Активирајте поделен екран со тековната апликација десно"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Активирајте поделен екран со тековната апликација лево"</string>
- <string name="system_multitasking_full_screen" msgid="336048080383640562">"Префрлете од поделен екран во цел екран"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Префрлете на апликацијата десно или долу при користењето поделен екран"</string>
- <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Префрлете на апликацијата лево или горе при користењето поделен екран"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Мултитаскинг"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Користете поделен екран со тековната апликација оддесно"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Користете поделен екран со тековната апликација одлево"</string>
+ <string name="system_multitasking_full_screen" msgid="336048080383640562">"Префрлете се од поделен екран на цел екран"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Префрлете се на апликацијата десно или долу при користењето поделен екран"</string>
+ <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Префрлете се на апликацијата лево или горе при користењето поделен екран"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"При поделен екран: префрлете ги аплик. од едната на другата страна"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Внесување"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Префрлете на следниот јазик"</string>
@@ -1285,7 +1290,7 @@
<string name="dismiss_dialog" msgid="2195508495854675882">"Отфрли"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Екранот е поврзан"</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>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Осветлување на тастатура"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d од %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за домот"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Брзо прист. до контр. за дом. како штедач на екран"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Контролите за домот како штедач на екран"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
index 0d81120..a8d9695 100644
--- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Исклучено"</item>
<item msgid="5137565285664080143">"Вклучено"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Недостапно"</item>
+ <item msgid="3079622119444911877">"Исклучено"</item>
+ <item msgid="3028994095749238254">"Вклучено"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 8ce3796..8e9392c 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"എല്ലാം കാണുക"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth ഉപയോഗിക്കുക"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"കണക്റ്റ് ചെയ്തു"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"സംരക്ഷിച്ചു"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"വിച്ഛേദിക്കുക"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"സജീവമാക്കുക"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"നാളെ വീണ്ടും സ്വയമേവ ഓണാക്കുക"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ക്വിക്ക് ഷെയർ, Find My Device, ഉപകരണ ലൊക്കേഷൻ എന്നിവ പോലുള്ള ഫീച്ചറുകൾ Bluetooth ഉപയോഗിക്കുന്നു"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth നാളെ 5 AM-ന് ഓണാക്കും"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ബാറ്ററി"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ഓഡിയോ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ഹെഡ്സെറ്റ്"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"പ്രശ്നം റെക്കോർഡ് ചെയ്യുക"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"ആരംഭിക്കുക"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"നിർത്തുക"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ബഗ് റിപ്പോർട്ട്"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"നിങ്ങളുടെ ഉപകരണ അനുഭവത്തിന്റെ ഏത് ഭാഗമാണ് ബാധിച്ചത്?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"പ്രശ്ന തരം തിരഞ്ഞെടുക്കുക"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"സ്ക്രീൻ റെക്കോർഡ്"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"ഇടത്തരം"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"കൂടുതൽ"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"പുതിയ ഉപകരണം ജോടിയാക്കുക"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"പുതിയ ഉപകരണം ജോടിയാക്കാൻ ക്ലിക്ക് ചെയ്യുക"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ഉപകരണ മൈക്രോഫോൺ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ഉപകരണ ക്യാമറ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ഉപകരണ ക്യാമറയോ മൈക്രോഫോണോ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"കീബോർഡ് ലേഔട്ട് മാറുക"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"അല്ലെങ്കിൽ"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"തിരയൽ ചോദ്യം മായ്ക്കുക"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"കുറുക്കുവഴികൾ"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"കീബോർഡ് കുറുക്കുവഴികൾ"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"കുറുക്കുവഴികൾ തിരയുക"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"കുറുക്കുവഴി കണ്ടെത്തിയില്ല"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"സിസ്റ്റം"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant തുറക്കുക"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ലോക്ക് സ്ക്രീൻ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ഒരു കുറിപ്പെടുക്കുക"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"സിസ്റ്റം മൾട്ടിടാസ്കിംഗ്"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"നിലവിലെ ആപ്പ് വലതുവശത്ത് വരുന്ന രീതിയിൽ സ്ക്രീൻ വിഭജന മോഡിൽ കടക്കുക"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"നിലവിലെ ആപ്പ് ഇടതുവശത്ത് വരുന്ന രീതിയിൽ സ്ക്രീൻ വിഭജന മോഡിൽ കടക്കുക"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"മൾട്ടിടാസ്കിംഗ്"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"വലതുവശത്തുള്ള നിലവിലെ ആപ്പിനൊപ്പം സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുക"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"ഇടതുവശത്തുള്ള നിലവിലെ ആപ്പിനൊപ്പം സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുക"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"സ്ക്രീൻ വിഭജന മോഡിൽ നിന്ന് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറുക"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുമ്പോൾ വലതുവശത്തെ/താഴത്തെ ആപ്പിലേക്ക് മാറൂ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുമ്പോൾ ഇടതുവശത്തെ/മുകളിലെ ആപ്പിലേക്ക് മാറൂ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 1a2e4b1f..aab22b3 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Бүгдийг харах"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-г ашиглах"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Холбогдсон"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Хадгалсан"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"салгах"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"идэвхжүүлэх"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Маргааш автоматаар дахин асаах"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Түргэн хуваалцах, Миний төхөөрөмжийг олох зэрэг онцлогууд болон төхөөрөмжийн байршил Bluetooth-г ашигладаг"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth маргааш ҮӨ 5 цагт асна"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батарей"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Асуудлыг бичих"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Эхлүүлэх"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Зогсоох"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Алдааны мэдээ"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Таны төхөөрөмжийн хэрэглээний аль хэсэгт нөлөөлсөн бэ?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Асуудлын төрөл сонгоно уу"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Дэлгэцийн бичлэг"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандарт"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Дунд зэрэг"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Өндөр"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Сонсголын төхөөрөмжүүд"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Сонсголын төхөөрөмжүүд"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Шинэ төхөөрөмж хослуулах"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Шинэ төхөөрөмж хослуулахын тулд товшино уу"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Төхөөрөмжийн микрофоныг блокоос гаргах уу?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Төхөөрөмжийн камерыг блокоос гаргах уу?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Төхөөрөмжийн камер болон микрофоныг блокоос гаргах уу?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Гарын бүдүүвч рүү сэлгэх"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"эсвэл"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Хайлтын асуулгыг арилгах"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Товчлолууд"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Товчлуурын шууд холбоос"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Товчлолууд хайх"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ямар ч товчлол олдсонгүй"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Систем"</string>
@@ -770,12 +775,12 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Туслахыг нээх"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Түгжээтэй дэлгэц"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Тэмдэглэл хөтлөх"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Систем олон ажил зэрэг хийх"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Одоогийн аппаар баруун гар талд дэлгэц хуваахад орох"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Одоогийн аппаар зүүн гар талд дэлгэц хуваахад орох"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Олон ажил зэрэг хийх"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Одоогийн аппыг баруун талд байгаагаар дэлгэцийг хуваахыг ашиглах"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Одоогийн аппыг зүүн талд байгаагаар дэлгэцийг хуваахыг ашиглах"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Дэлгэц хуваахаас бүтэн дэлгэц рүү сэлгэх"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Дэлгэц хуваахыг ашиглаж байхдаа баруун эсвэл доор байх апп руу сэлгэ"</string>
- <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Дэлгэц хуваахыг ашиглаж байхдаа зүүн эсвэл дээр байх апп руу сэлгэ"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Дэлгэц хуваахыг ашиглаж байхдаа баруун талд эсвэл доор байх апп руу сэлгэ"</string>
+ <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Дэлгэц хуваахыг ашиглаж байхдаа зүүн талд эсвэл дээр байх апп руу сэлгэ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Дэлгэц хуваах үеэр: аппыг нэгээс нөгөөгөөр солих"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Оролт"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Дараагийн хэл рүү сэлгэх"</string>
@@ -1268,7 +1273,7 @@
<string name="call_from_work_profile_action" msgid="2937701298133010724">"Ажлын профайл руу сэлгэх"</string>
<string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"Ажлын гар утасны апп суулгах"</string>
<string name="call_from_work_profile_close" msgid="5830072964434474143">"Цуцлах"</string>
- <string name="lock_screen_settings" msgid="6152703934761402399">"Түгжигдсэн дэлгэцийг өөрчлөх"</string>
+ <string name="lock_screen_settings" msgid="6152703934761402399">"Түгжээтэй дэлгэцийг өөрчлөх"</string>
<string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Түгжээтэй дэлгэцийг өөрчлөхийн тулд түгжээг тайлна уу"</string>
<string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi боломжгүй байна"</string>
<string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камерыг блоклосон"</string>
diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
index cfaf693..a3f5454 100644
--- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Унтраалттай"</item>
<item msgid="5137565285664080143">"Асаалттай"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Боломжгүй"</item>
+ <item msgid="3079622119444911877">"Унтраалттай"</item>
+ <item msgid="3028994095749238254">"Асаалттай"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 2e21fd0..071935a 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"सर्व पहा"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लूटूथ वापरा"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट केले"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव्ह केले"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट करा"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ॲक्टिव्हेट करा"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"उद्या पुन्हा आपोआप सुरू करा"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"क्विक शेअर, Find My Device आणि डिव्हाइसचे स्थान यांसारखी वैशिष्ट्ये ब्लूटूथ वापरतात"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ब्लूटूथ उद्या सकाळी ५ वाजता सुरू होईल"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बॅटरी"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"समस्या रेकॉर्ड करा"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"सुरुवात करा"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"थांबवा"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"बग रिपोर्ट"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"तुमच्या डिव्हाइसबाबत कोणत्या अनुभवावर परिणाम झाला?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्येचा प्रकार निवडा"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रीन रेकॉर्ड"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"साधारण"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"मध्यम"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"उच्च"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"श्रवणयंत्रे"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"श्रवणयंत्रे"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नवीन डिव्हाइस पेअर करा"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नवीन डिव्हाइस पेअर करण्यासाठी क्लिक करा"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिव्हाइसचा मायक्रोफोन अनब्लॉक करायचा आहे का?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिव्हाइसचा कॅमेरा अनब्लॉक करायचा आहे का?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिव्हाइसचा कॅमेरा आणि मायक्रोफोन अनब्लॉक करायचा आहे का?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"कीबोर्ड लेआउट स्विच करा"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"किंवा"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"शोध क्वेरी साफ करा"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"शॉर्टकट"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"कीबोर्ड शॉर्टकट"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"शॉर्टकट शोधा"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"एकही शॉर्टकट आढळला नाहीत"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"सिस्टीम"</string>
@@ -757,7 +762,7 @@
<string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"इनपुट शॉर्टकट दाखवत आहे"</string>
<string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"ॲप्स उघडणारे शॉर्टकट दाखवत आहे"</string>
<string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"सद्य अॅपसाठी शॉर्टकट दाखवत आहे"</string>
- <string name="group_system_access_notification_shade" msgid="1619028907006553677">"सूचना पहा"</string>
+ <string name="group_system_access_notification_shade" msgid="1619028907006553677">"नोटिफिकेशन पहा"</string>
<string name="group_system_full_screenshot" msgid="5742204844232667785">"स्क्रीनशॉट घ्या"</string>
<string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"शॉर्टकट दाखवा"</string>
<string name="group_system_go_back" msgid="2730322046244918816">"मागे जा"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant उघडा"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"लॉक स्क्रीन"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"नोंद घ्या"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"सिस्टीम मल्टिटास्किंग"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"उजव्या बाजूला सध्याचे अॅप असलेल्या स्प्लिट स्क्रीनवर जा"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"डाव्या बाजूला सध्याचे अॅप असलेल्या स्प्लिट स्क्रीनवर जा"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"मल्टिटास्किंग"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"सद्य ॲप उजवीकडे ठेवून स्प्लिट स्क्रीन वापरा"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"सद्य ॲप डावीकडे ठेवून स्प्लिट स्क्रीन वापरा"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"स्प्लिट स्क्रीनवरून फुल स्क्रीनवर स्विच करा"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रीन वापरताना उजवीकडील किंवा खालील अॅपवर स्विच करा"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रीन वापरताना डावीकडील किंवा वरील अॅपवर स्विच करा"</string>
diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
index abb7ace..54c320c 100644
--- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"बंद आहे"</item>
<item msgid="5137565285664080143">"सुरू आहे"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"उपलब्ध नाही"</item>
+ <item msgid="3079622119444911877">"बंद आहे"</item>
+ <item msgid="3028994095749238254">"सुरू आहे"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 9ae774f..95d4237 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Lihat semua"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Gunakan Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Disambungkan"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan sambungan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Dihidupkan sekali lagi esok secara automatik"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Ciri seperti Quick Share, Find My Device dan lokasi peranti menggunakan Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth akan dihidupkan esok pada pukul 5 PG"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Set Kepala"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Rekodkan masalah"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Mula"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Hentikan"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Laporan Pepijat"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Pengalaman peranti yang manakah yang terjejas?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rakam skrin"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Sederhana"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Tinggi"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Peranti pendengaran"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Peranti pendengaran"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Gandingkan peranti baharu"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menggandingkan peranti baharu"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Nyahsekat mikrofon peranti?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Nyahsekat kamera peranti?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Nyahsekat kamera dan mikrofon peranti?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Tukar reka letak papan kekunci"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"atau"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Kosongkan pertanyaan carian"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Pintasan"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Pintasan Papan Kekunci"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Cari pintasan"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Tiada pintasan ditemukan"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistem"</string>
@@ -762,16 +768,16 @@
<string name="group_system_go_back" msgid="2730322046244918816">"Kembali"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"Akses skrin utama"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"Lihat apl terbaharu"</string>
- <string name="group_system_cycle_forward" msgid="5478663965957647805">"Kitar ke hadapan menerusi apl terbaharu"</string>
- <string name="group_system_cycle_back" msgid="8194102916946802902">"Kitar ke belakang menerusi apl terbaharu"</string>
+ <string name="group_system_cycle_forward" msgid="5478663965957647805">"Navigasi apl terbaharu ke arah kanan"</string>
+ <string name="group_system_cycle_back" msgid="8194102916946802902">"Navigasi apl terbaharu ke arah kiri"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Buka senarai apl"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Buka tetapan"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Buka Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kunci skrin"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Catat nota"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Berbilang tugas sistem"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Masuk skrin pisah dengan apl semasa pada sisi kanan"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Masuk skrin pisah dengan apl semasa pada sisi kiri"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Berbilang tugas"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gunakan skrin pisah dengan apl semasa pada sebelah kanan"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gunakan skrin pisah dengan apl semasa pada sebelah kiri"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Beralih daripada skrin pisah kepada skrin penuh"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Tukar kepada apl di sebelah kanan/bawah semasa menggunakan skrin pisah"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Tukar kepada apl di sebelah kiri/atas semasa menggunakan skrin pisah"</string>
@@ -1124,7 +1130,7 @@
<string name="basic_status" msgid="2315371112182658176">"Buka perbualan"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"Widget perbualan"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"Ketik perbualan untuk menambahkan perbualan itu pada skrin Utama anda"</string>
- <string name="no_conversations_text" msgid="5354115541282395015">"Perbualan terbaharu anda akan dipaparkan di sini"</string>
+ <string name="no_conversations_text" msgid="5354115541282395015">"Perbualan terbaharu akan dipaparkan di sini"</string>
<string name="priority_conversations" msgid="3967482288896653039">"Perbualan keutamaan"</string>
<string name="recent_conversations" msgid="8531874684782574622">"Perbualan terbaharu"</string>
<string name="days_timestamp" msgid="5821854736213214331">"<xliff:g id="DURATION">%1$s</xliff:g> hari lalu"</string>
@@ -1193,7 +1199,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# apl aktif}other{# apl aktif}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Maklumat baharu"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apl aktif"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Apl ini aktif dan berfungsi walaupun anda tidak menggunakannya. Ini meningkatkan kefungsian apl tetapi mungkin akan memberikan kesan kepada hayat bateri."</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Apl ini aktif dan berfungsi walaupun anda tidak menggunakannya. Hal ini meningkatkan kefungsian apl tetapi akan memberikan kesan kepada hayat bateri."</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Berhenti"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Dihentikan"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Selesai"</string>
@@ -1304,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tahap %1$d daripada %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kawalan Rumah"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Akses kawalan rumah anda sebagai penyelamat skrin dengan cepat"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Jadikan kawalan rumah anda sebagai penyelamat skrin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 1249885..7941935 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -235,7 +235,7 @@
<string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"အကြောင်းကြားစာအကွက်"</string>
<string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"အမြန်လုပ် အပြင်အဆင်"</string>
<string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"‘အမြန်ဆက်တင်များ’ နှင့် ‘အကြောင်းကြားစာအကွက်’။"</string>
- <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"မျက်နှာပြင် သော့ပိတ်ရန်"</string>
+ <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"လော့ခ်မျက်နှာပြင်"</string>
<string name="accessibility_desc_work_lock" msgid="4355620395354680575">"အလုပ်သုံး လော့ခ်မျက်နှာပြင်"</string>
<string name="accessibility_desc_close" msgid="8293708213442107755">"ပိတ်ရန်"</string>
<string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"လုံးဝ အသံပိတ်ထားရန်"</string>
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"အားလုံးကြည့်ရန်"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ဘလူးတုသ်သုံးရန်"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ချိတ်ဆက်ထားသည်"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"သိမ်းထားသည်"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"စသုံးရန်"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"မနက်ဖြန် အလိုအလျောက် ထပ်ဖွင့်ရန်"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"‘အမြန် မျှဝေပါ’၊ Find My Device နှင့် စက်ပစ္စည်းတည်နေရာကဲ့သို့ တူးလ်များသည် ဘလူးတုသ်သုံးသည်"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"မနက်ဖြန် မနက် ၅ နာရီတွင် ဘလူးတုသ်ကို ဖွင့်ပါမည်"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ဘက်ထရီ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"ပြဿနာကို မှတ်တမ်းတင်ခြင်း"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"စတင်ပါ"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"ရပ်ပါ"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ချွတ်ယွင်းမှု အစီရင်ခံစာ"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"စက်အသုံးပြုမှု၏ မည်သည့်အပိုင်းကို သက်ရောက်သလဲ။"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ပြဿနာအမျိုးအစား ရွေးရန်"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ဖန်သားပြင်ရိုက်ကူးရန်"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"အသင့်အတင့်"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"များ"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"နားကြားကိရိယာ"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"နားကြားကိရိယာ"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"စက်အသစ်တွဲချိတ်ရန်"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"စက်အသစ် တွဲချိတ်ရန် နှိပ်ပါ"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"စက်၏မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"စက်၏ကင်မရာကို ပြန်ဖွင့်မလား။"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"စက်၏ကင်မရာနှင့် မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string>
@@ -550,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ဤစက်ပစ္စည်းကို သင့်မိဘက စီမံခန့်ခွဲသည်။ သင့်မိဘက သင်သုံးသောအက်ပ်များ၊ သင်၏တည်နေရာနှင့် အသုံးပြုချိန် ကဲ့သို့သော အချက်အလက်များကို မြင်နိုင်ပြီး စီမံခန့်ခွဲနိုင်သည်။"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ဖြင့် ဆက်ဖွင့်ထားရန်"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"စက်လော့ခ်ကျနေသည်၊ အထောက်အထားစိစစ်ရန် ကြိုးပမ်းမှုအကြိမ်ရေ များလွန်းသည်"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"လော့ခ်ကျနေသည်၊ အထောက်အထားစိစစ်ရန် ကြိုးပမ်းကြိမ် များလွန်းသည်"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"စက်လော့ခ်ကျနေသည်\nအထောက်အထားစိစစ်၍ မရပါ"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>။ <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"အသံဆက်တင်များ"</string>
@@ -593,7 +599,7 @@
<string name="stream_accessibility" msgid="3873610336741987152">"အများသုံးနိုင်မှု"</string>
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"အသံမြည်သည်"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"တုန်ခါသည်"</string>
- <string name="volume_ringer_status_silent" msgid="3691324657849880883">"အသံတိတ်သည်"</string>
+ <string name="volume_ringer_status_silent" msgid="3691324657849880883">"အသံပိတ်ရန်"</string>
<string name="media_device_cast" msgid="4786241789687569892">"ကာစ်လုပ်ရန်"</string>
<string name="stream_notification_unavailable" msgid="4313854556205836435">"ဖုန်းမြည်သံပိတ်ထားသဖြင့် မရနိုင်ပါ"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"‘မနှောင့်ယှက်ရ’ ဖွင့်ထားသောကြောင့် မရနိုင်ပါ"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"ကီးဘုတ်အပြင်အဆင် ပြောင်းခြင်း"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"သို့မဟုတ်"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ရှာဖွေစာလုံး ရှင်းထုတ်ရန်"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ဖြတ်လမ်းလင့်ခ်များ"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"လက်ကွက်ဖြတ်လမ်းများ"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ဖြတ်လမ်းလင့်ခ်များ ရှာပါ"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ဖြတ်လမ်းလင့်ခ် မတွေ့ပါ"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"စနစ်"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant ဖွင့်ရန်"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"လော့ခ်မျက်နှာပြင်"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"မှတ်စုရေးရန်"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"စနစ်က တစ်ပြိုင်နက် များစွာလုပ်ခြင်း"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"လက်ရှိအက်ပ်ကို မျက်နှာပြင် ခွဲ၍ပြသမှု၏ ညာဘက်တွင်ထည့်ရန်"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"လက်ရှိအက်ပ်ကို မျက်နှာပြင် ခွဲ၍ပြသမှု၏ ဘယ်ဘက်တွင်ထည့်ရန်"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"တစ်ပြိုင်နက် များစွာလုပ်ခြင်း"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"လက်ရှိအက်ပ်ကို ညာ၌ထားကာ မျက်နှာပြင် ခွဲ၍ပြသခြင်း သုံးရန်"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"လက်ရှိအက်ပ်ကို ဘယ်၌ထားကာ မျက်နှာပြင် ခွဲ၍ပြသခြင်း သုံးရန်"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"မျက်နှာပြင် ခွဲ၍ပြသမှုမှ မျက်နှာပြင်အပြည့်သို့ ပြောင်းရန်"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"မျက်နှာပြင်ခွဲ၍ပြသခြင်း သုံးစဉ် ညာ (သို့) အောက်ရှိအက်ပ်သို့ ပြောင်းရန်"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသုံးစဉ် ဘယ် (သို့) အထက်ရှိအက်ပ်သို့ ပြောင်းရန်"</string>
@@ -867,7 +873,7 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ဆက်တင်များ၏ အစီအစဉ်ကို တည်းဖြတ်ပါ။"</string>
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ပါဝါမီနူး"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"စာမျက်နှာ <xliff:g id="ID_2">%2$d</xliff:g> အနက်မှ စာမျက်နှာ <xliff:g id="ID_1">%1$d</xliff:g>"</string>
- <string name="tuner_lock_screen" msgid="2267383813241144544">"လော့ခ်ချထားချိန် မျက်နှာပြင်"</string>
+ <string name="tuner_lock_screen" msgid="2267383813241144544">"လော့ခ်မျက်နှာပြင်"</string>
<string name="finder_active" msgid="7907846989716941952">"ပါဝါပိတ်ထားသော်လည်း Find My Device ဖြင့် ဤဖုန်းကို ရှာနိုင်သည်"</string>
<string name="shutdown_progress" msgid="5464239146561542178">"စက်ပိတ်နေသည်…"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ဂရုပြုစရာ အဆင့်များ ကြည့်ရန်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 0f9bb46..59afb3e 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Se alle"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bruk Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Tilkoblet"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Lagret"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koble fra"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiver"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Slå på igjen i morgen automatisk"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funksjoner som Quick Share, Finn enheten min og enhetsposisjon bruker Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth slås på i morgen kl. 05.00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Registrer problem"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stopp"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Feilrapport"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del av enhetsopplevelsen din ble påvirket?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Velg problemtype"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjermopptak"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Middels"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Høy"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Høreapparater"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Koble til en ny enhet"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klikk for å koble til en ny enhet"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du oppheve blokkeringen av enhetsmikrofonen?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du oppheve blokkeringen av enhetskameraet?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du oppheve blokkeringen av enhetskameraet og -mikrofonen?"</string>
@@ -551,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Denne enheten administreres av forelderen din. Forelderen din kan se og administrere informasjon, for eksempel appene du bruker, posisjonen din og skjermtiden din."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Holdes opplåst med TrustAgent"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Enheten var låst – for mange autentiseringsforsøk"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Enheten er låst – for mange autentiseringsforsøk"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Enheten er låst\nKunne ikke autentisere"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Lydinnstillinger"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Bytt tastaturoppsett"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"eller"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Fjern søket"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Hurtigtaster"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Hurtigtaster"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Søk etter hurtigtaster"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Fant ingen hurtigtaster"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"System"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Åpne assistenten"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Låseskjerm"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Ta et notat"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking på systemet"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Åpne delt skjerm med den aktive appen til høyre"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Åpne delt skjerm med den aktive appen til venstre"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Bruk delt skjerm med den nåværende appen til høyre"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Bruk delt skjerm med den nåværende appen til venstre"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Bytt fra delt skjerm til fullskjerm"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bytt til appen til høyre eller under mens du bruker delt skjerm"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bytt til appen til venstre eller over mens du bruker delt skjerm"</string>
diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
index af00423..a9efd1d 100644
--- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Av"</item>
<item msgid="5137565285664080143">"På"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Ikke tilgjengelig"</item>
+ <item msgid="3079622119444911877">"Av"</item>
+ <item msgid="3028994095749238254">"På"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 5f51a91..3ab647f 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -62,7 +62,7 @@
<string name="share_wifi_button_text" msgid="1285273973812029240">"Wi‑Fi सेयर गर्नुहोस्"</string>
<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>
+ <string name="wifi_debugging_always" msgid="2968383799517975155">"यस नेटवर्कमा सधैँ अनुमति दिनुहोस्"</string>
<string name="wifi_debugging_allow" msgid="4573224609684957886">"अनुमति दिनुहोस्"</string>
<string name="wifi_debugging_secondary_user_title" msgid="2493201475880517725">"वायरलेस डिबगिङ सेवालाई अनुमति दिइएको छैन"</string>
<string name="wifi_debugging_secondary_user_message" msgid="9085779370142222881">"हालैमा यस डिभाइसमा साइन इन भएका प्रयोगकर्ताले USB डिबगिङ सक्रिय गर्न सक्दैनन्। यो सुविधा प्रयोग गर्न कृपया खाताका एड्मिनका रूपमा साइन इन गर्नुहोस्।"</string>
@@ -108,7 +108,7 @@
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"तपाईंले रेकर्ड गर्दै गर्दा Android ले तपाईंको स्क्रिनमा देखिने वा डिभाइसमा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"तपाईंले कुनै एप रेकर्ड गर्दै गर्दा Android ले उक्त एपमा देखाइने वा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"रेकर्ड गर्न थाल्नुहोस्"</string>
- <string name="screenrecord_audio_label" msgid="6183558856175159629">"अडियो रेकर्ड गरियोस्"</string>
+ <string name="screenrecord_audio_label" msgid="6183558856175159629">"अडियो रेकर्ड गर्नुहोस्"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"डिभाइसको अडियो"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"तपाईंको डिभाइसका सङ्गीत, कल र रिङटोन जस्ता साउन्ड"</string>
<string name="screenrecord_mic_label" msgid="2111264835791332350">"माइक्रोफोन"</string>
@@ -116,7 +116,7 @@
<string name="screenrecord_continue" msgid="4055347133700593164">"सुरु गर्नुहोस्"</string>
<string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"स्क्रिन रेकर्ड गरिँदै छ"</string>
<string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"स्क्रिन र अडियो रेकर्ड गरिँदै छ"</string>
- <string name="screenrecord_taps_label" msgid="1595690528298857649">"स्पर्श गरिएका स्थानहरू देखाइयोस्"</string>
+ <string name="screenrecord_taps_label" msgid="1595690528298857649">"स्पर्श गरिएका स्थानहरू देखाउनुहोस्"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"रोक्नुहोस्"</string>
<string name="screenrecord_share_label" msgid="5025590804030086930">"सेयर गर्नुहोस्"</string>
<string name="screenrecord_save_title" msgid="1886652605520893850">"स्क्रिन रेकर्डिङ सेभ गरियो"</string>
@@ -270,17 +270,25 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"सबै डिभाइसहरू हेर्नुहोस्"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लुटुथ प्रयोग गर्नुहोस्"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट गरिएको छ"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेभ गरिएको छ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट गर्नुहोस्"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"एक्टिभेट गर्नुहोस्"</string>
- <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"भोलि फेरि स्वतः अन गरियोस्"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"क्विक सेयर, Find My Device र डिभाइसको लोकेसन जस्ता सुविधाहरूले ब्लुटुथ प्रयोग गर्छन्"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ब्लुटुथ भोलि बिहान ५ बजे अन हुने छ"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"भोलि फेरि स्वतः अन गर्नुहोस्"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ब्याट्री"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string>
- <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"श्रवण यन्त्रहरू"</string>
+ <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"हियरिङ डिभाइसहरू"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"सक्रिय गर्दै…"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"अटो रोटेट"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रिन स्वतःघुम्ने"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"समस्या रेकर्ड गर्नुहोस्"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"सुरु गर्नुहोस्"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"रोक्नुहोस्"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"त्रुटिको रिपोर्ट"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"तपाईंको डिभाइसको कुन चाहिँ सुविधा प्रभावित भएको छ?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्याको प्रकार चयन गर्नुहोस्"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रिन रेकर्ड"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"डिफल्ट"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"मध्यम"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"उच्च"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"हियरिङ डिभाइसहरू"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"हियरिङ डिभाइसहरू"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नयाँ डिभाइस कनेक्ट गर्नुहोस्"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नयाँ डिभाइसमा कनेक्ट गर्न क्लिक गर्नुहोस्"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिभाइसको माइक्रोफोन अनब्लक गर्ने हो?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिभाइसको क्यामेरा अनब्लक गर्ने हो?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिभाइसको क्यामेरा र माइक्रोफोन अनब्लक गर्ने हो?"</string>
@@ -551,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"यो डिभाइस तपाईंका अभिभावक व्यवस्थापन गर्नुहुन्छ। तपाईंका अभिभावक तपाईंले प्रयोग गर्ने एप, तपाईंको स्थान र तपाईंले यन्त्र चलाएर बिताउने समय जस्ता जानकारी हेर्न तथा व्यवस्थापन गर्न सक्नुहुन्छ।"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ले खुला राखेको"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"डिभाइस लक गरिएको छ, प्रमाणीकरण गर्ने निकै धेरै प्रयास गरिएका छन्"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"डिभाइस लक गरिएको छ, निकै धेरै पटक प्रमाणीकरण गर्ने प्रयास भएको छ"</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"डिभाइस लक गरिएको छ\nप्रमाणीकरण गर्न सकिएन"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ध्वनिसम्बन्धी सेटिङहरू"</string>
@@ -631,7 +636,7 @@
<string name="status_bar_alarm" msgid="87160847643623352">"अलार्म"</string>
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_empty_state_label" msgid="7776761245237530394">"फोनमार्फत अझ छिटो र थप सुरक्षित तरिकाले खरिद गर्न भुक्तानी विधि सेटअप गर्नुहोस्"</string>
- <string name="wallet_app_button_label" msgid="7123784239111190992">"सबै देखाइयोस्"</string>
+ <string name="wallet_app_button_label" msgid="7123784239111190992">"सबै देखाउनुहोस्"</string>
<string name="wallet_secondary_label_no_card" msgid="8488069304491125713">"खोल्न ट्याप गर्नुहोस्"</string>
<string name="wallet_secondary_label_updating" msgid="5726130686114928551">"अपडेट गरिँदै छ"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"यो वालेट प्रयोग गर्न डिभाइस अनलक गर्नुहोस्"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"किबोर्डको लेआउट बदल्नुहोस्"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"वा"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"किवर्ड हटाउनुहोस्"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"सर्टकटहरू"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"किबोर्डका सर्टकटहरू"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"सर्टकटहरू खोज्नुहोस्"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"कुनै पनि सर्टकट भेटिएन"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"सिस्टम"</string>
@@ -759,7 +764,7 @@
<string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"हालको एपका लागि सर्टकटहरू देखाइँदै छ"</string>
<string name="group_system_access_notification_shade" msgid="1619028907006553677">"सूचनाहरू हेर्नुहोस्"</string>
<string name="group_system_full_screenshot" msgid="5742204844232667785">"स्क्रिनसट खिच्नुहोस्"</string>
- <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"सर्टकटहरू देखाइऊन्"</string>
+ <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"सर्टकटहरू देखाउनुहोस्"</string>
<string name="group_system_go_back" msgid="2730322046244918816">"पछाडि जानुहोस्"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"होम स्क्रिनमा जानुहोस्"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"हालसालै चलाइएका एपहरू हेर्ने तरिका"</string>
@@ -769,10 +774,10 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"सेटिङ खोल्नुहोस्"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"एसिस्टेन्ट खोल्नुहोस्"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"स्क्रिन लक गर्नुहोस्"</string>
- <string name="group_system_quick_memo" msgid="3764560265935722903">"नोट लेख"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"सिस्टम मल्टिटास्किङ"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"हालको एप दायाँतर्फ रहने गरी स्प्लिट स्क्रिन मोड सुरु गर्नुहोस्"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"हालको एप बायाँतर्फ रहने गरी स्प्लिट स्क्रिन मोड सुरु गर्नुहोस्"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"नोट लेख्नुहोस्"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"एकै पटक एकभन्दा बढी एप चलाउन मिल्ने सुविधा"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"हालको एप दायाँ भागमा पारेर स्प्लिट स्क्रिन प्रयोग गर्नुहोस्"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"हालको एप बायाँ भागमा पारेर स्प्लिट स्क्रिन प्रयोग गर्नुहोस्"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"स्प्लिट स्क्रिनको साटो फुल स्क्रिन प्रयोग गर्नुहोस्"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रिन प्रयोग गर्दै गर्दा दायाँ वा तलको एप चलाउनुहोस्"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रिन प्रयोग गर्दै गर्दा बायाँ वा माथिको एप चलाउनुहोस्"</string>
@@ -1288,7 +1293,7 @@
<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>
+ <string name="privacy_dialog_expand_action" msgid="9129262348628331377">"एक्स्पान्ड गर्नुहोस् र विकल्पहरू देखाउनुहोस्"</string>
<string name="privacy_dialog_collapse_action" msgid="277419962019466347">"कोल्याप्स गर्नुहोस्"</string>
<string name="privacy_dialog_close_app_button" msgid="8006250171305878606">"यो एप बन्द गर्नुहोस्"</string>
<string name="privacy_dialog_close_app_message" msgid="1316408652526310985">"<xliff:g id="APP_NAME">%1$s</xliff:g> बन्द गरिएको छ"</string>
diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
index 005a473..c1b2f34 100644
--- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"अफ छ"</item>
<item msgid="5137565285664080143">"अन छ"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"उपलब्ध छैन"</item>
+ <item msgid="3079622119444911877">"अफ छ"</item>
+ <item msgid="3028994095749238254">"अन छ"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 1f780b3..e85a1da 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -145,7 +145,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefoon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Spraakassistent"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Portemonnee"</string>
- <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-codescanner"</string>
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-codescanner"</string>
<string name="accessibility_unlock_button" msgid="3613812140816244310">"Ontgrendeld"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Apparaat vergrendeld"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Gezicht scannen"</string>
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alles tonen"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth gebruiken"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbonden"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Opgeslagen"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"loskoppelen"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activeren"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen weer automatisch aanzetten"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Functies zoals Quick Share, Vind mijn apparaat en apparaatlocatie maken gebruik van bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth gaat morgen om 05:00 uur aan"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterijniveau"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Probleem vastleggen"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Starten"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppen"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bugrapport"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Op welk onderdeel van de apparaatfunctionaliteit had dit effect?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Probleemtype selecteren"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Schermopname"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standaard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Gemiddeld"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Hoog"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hoortoestellen"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hoortoestellen"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Nieuw apparaat koppelen"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nieuw apparaat te koppelen"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Microfoon van apparaat niet meer blokkeren?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Apparaatcamera niet meer blokkeren?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blokkeren van apparaatcamera en -microfoon opheffen?"</string>
@@ -551,7 +556,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dit apparaat wordt beheerd door je ouder. Je ouder kan informatie bekijken en beheren, zoals de apps die je gebruikt, je locatie en je schermtijd."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ontgrendeld gehouden door TrustAgent"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Het apparaat is vergrendeld na te veel verificatiepogingen"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Te veel pogingen. Apparaat vergrendeld."</string>
<string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Apparaat vergrendeld\nVerificatie mislukt"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Geluidsinstellingen"</string>
@@ -608,7 +613,7 @@
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Ruimtelijke audio"</string>
<string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Uit"</string>
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Vast"</string>
- <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hoofdbeweging volgen"</string>
+ <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hoofdbeweging volgen"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tik om de beltoonmodus te wijzigen"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"geluid uit"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"geluid aanzetten"</string>
@@ -637,7 +642,7 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontgrendelen om te gebruiken"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Er is een probleem opgetreden bij het ophalen van je kaarten. Probeer het later opnieuw."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Instellingen voor vergrendelscherm"</string>
- <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR-codescanner"</string>
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR-codescanner"</string>
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updaten"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Toetsenbordindeling wisselen"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"of"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Zoekopdracht wissen"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Sneltoetsen"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Sneltoetsen"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Sneltoetsen zoeken"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Geen sneltoetsen gevonden"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Systeem"</string>
@@ -770,12 +775,12 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistent openen"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Scherm vergrendelen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Notitie maken"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Systeem-multitasking"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Gesplitst scherm openen met huidige app rechts"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Gesplitst scherm openen met huidige app links"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasken"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gesplitst scherm gebruiken met de huidige app aan de rechterkant"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gesplitst scherm gebruiken met de huidige app aan de linkerkant"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Van gesplitst scherm naar volledig scherm schakelen"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Ga naar de app rechts of onderaan als je een gesplitst scherm gebruikt"</string>
- <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ga naar de app links of bovenaan als je een gesplitst scherm gebruikt"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Naar de app rechts of onderaan gaan als je een gesplitst scherm gebruikt"</string>
+ <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Naar de app links of bovenaan gaan als je een gesplitst scherm gebruikt"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Tijdens gesplitst scherm: een app vervangen door een andere"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Invoer"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Overschakelen naar volgende taal"</string>
@@ -829,8 +834,8 @@
<string name="right_keycode" msgid="2480715509844798438">"Toetscode rechts"</string>
<string name="left_icon" msgid="5036278531966897006">"Icoon links"</string>
<string name="right_icon" msgid="1103955040645237425">"Icoon rechts"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"Houd vast en sleep om tegels toe te voegen"</string>
- <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Houd vast en sleep om tegels opnieuw in te delen"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"Houd een tegel ingedrukt en sleep om die toe te voegen"</string>
+ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Houd een tegel ingedrukt en sleep om die te verplaatsen"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Sleep hier naartoe om te verwijderen"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Je hebt minimaal <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tegels nodig"</string>
<string name="qs_edit" msgid="5583565172803472437">"Bewerken"</string>
@@ -1262,7 +1267,7 @@
<string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Nog <xliff:g id="PERCENTAGE">%s</xliff:g> batterijlading"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Verbind je stylus met een oplader"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Batterij van stylus bijna leeg"</string>
- <string name="video_camera" msgid="7654002575156149298">"Videocamera"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Videocamera"</string>
<string name="call_from_work_profile_title" msgid="5418253516453177114">"Kan niet bellen vanuit een app voor persoonlijke doeleinden"</string>
<string name="call_from_work_profile_text" msgid="2856337395968118274">"Je organisatie staat je alleen toe om te bellen vanuit werk-apps"</string>
<string name="call_from_work_profile_action" msgid="2937701298133010724">"Overschakelen naar werkprofiel"</string>
diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
index 1b286f3..c5d9361 100644
--- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Uit"</item>
<item msgid="5137565285664080143">"Aan"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Niet beschikbaar"</item>
+ <item msgid="3079622119444911877">"Uit"</item>
+ <item msgid="3028994095749238254">"Aan"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 9a602b8..77ed062 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ସବୁ ଦେଖନ୍ତୁ"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"କନେକ୍ଟ କରାଯାଇଛି"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ସେଭ କରାଯାଇଛି"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ଡିସକନେକ୍ଟ କରନ୍ତୁ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ଚାଲୁ କରନ୍ତୁ"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ଆସନ୍ତାକାଲି ସ୍ୱତଃ ପୁଣି ଚାଲୁ ହେବ"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, Find My Device ଏବଂ ଡିଭାଇସ ଲୋକେସନ ପରି ଫିଚରଗୁଡ଼ିକ ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରେ"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ବ୍ଲୁଟୁଥ ଆସନ୍ତାକାଲି 5 AMରେ ଚାଲୁ ହେବ"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ବ୍ୟାଟେରୀ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"ସମସ୍ୟାର ରେକର୍ଡ କରନ୍ତୁ"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ବଗ୍ ରିପୋର୍ଟ୍"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ଆପଣଙ୍କ ଡିଭାଇସ ଅନୁଭୂତିର କେଉଁ ଅଂଶ ପ୍ରଭାବିତ ହୋଇଛି?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ସମସ୍ୟାର ପ୍ରକାର ଚୟନ କରନ୍ତୁ"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ସ୍କ୍ରିନ ରେକର୍ଡ"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ଷ୍ଟାଣ୍ଡାର୍ଡ"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"ମଧ୍ୟମ"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"ଅଧିକ"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ହିଅରିଂ ଡିଭାଇସଗୁଡ଼ିକ"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ହିଅରିଂ ଡିଭାଇସଗୁଡ଼ିକ"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ନୂଆ ଡିଭାଇସ ପେୟାର କର"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ନୂଆ ଡିଭାଇସ ପେୟାର କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ଡିଭାଇସର ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ କରିବେ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ଡିଭାଇସର କେମେରାକୁ ଅନବ୍ଲକ କରିବେ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ଡିଭାଇସର କ୍ୟାମେରା ଏବଂ ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ୍ କରିବେ?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"କୀ\'ବୋର୍ଡ୍ର ଲେଆଉଟ୍କୁ ବଦଳାନ୍ତୁ"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"କିମ୍ବା"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ସର୍ଚ୍ଚ କ୍ୱେରୀକୁ ଖାଲି କରନ୍ତୁ"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ସର୍ଟକଟଗୁଡ଼ିକ"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"କୀବୋର୍ଡ ସର୍ଟକଟ"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ସର୍ଟକଟ ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"କୌଣସି ସର୍ଟକଟ ମିଳିଲା ନାହିଁ"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"ସିଷ୍ଟମ"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant ଖୋଲନ୍ତୁ"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ଲକ ସ୍କ୍ରିନ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ଏକ ନୋଟ ଲେଖନ୍ତୁ"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ସିଷ୍ଟମ ମଲ୍ଟିଟାସ୍କିଂ"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"RHSରେ ବର୍ତ୍ତମାନର ଆପ ସହ ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନକୁ ପ୍ରବେଶ କରାନ୍ତୁ"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"LHSରେ ବର୍ତ୍ତମାନର ଆପ ସହ ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନକୁ ପ୍ରବେଶ କରାନ୍ତୁ"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ମଲ୍ଟିଟାସ୍କିଂ"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"ଡାହାଣରେ ବର୍ତ୍ତମାନର ଆପ ସହିତ ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନକୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"ବାମରେ ବର୍ତ୍ତମାନର ଆପ ସହିତ ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନକୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନରୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ଡାହାଣପଟର ବା ତଳର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ବାମପଟର ବା ଉପରର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"କୀବୋର୍ଡ ବେକଲାଇଟ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dରୁ %1$d ନମ୍ବର ଲେଭେଲ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ହୋମ କଣ୍ଟ୍ରୋଲ୍ସ"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"ସ୍କ୍ରିନସେଭର ଭାବେ ହୋମ କଣ୍ଟ୍ରୋଲ୍ସକୁ ଶୀଘ୍ର ଆକ୍ସେସ କର"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"ସ୍କ୍ରିନସେଭର ଭାବେ ହୋମ କଣ୍ଟ୍ରୋଲ୍ସକୁ ଶୀଘ୍ର ଆକ୍ସେସ କରନ୍ତୁ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml
index fd727bf..fe187c2 100644
--- a/packages/SystemUI/res/values-or/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"ବନ୍ଦ ଅଛି"</item>
<item msgid="5137565285664080143">"ଚାଲୁ ଅଛି"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"ଅନୁପଲବ୍ଧ"</item>
+ <item msgid="3079622119444911877">"ବନ୍ଦ ଅଛି"</item>
+ <item msgid="3028994095749238254">"ଚାଲୁ ଅଛି"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 40efd0b..0cc753b 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ਸਭ ਦੇਖੋ"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ਬਲੂਟੁੱਥ ਵਰਤੋ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ਕਨੈਕਟ ਹੈ"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ਕਿਰਿਆਸ਼ੀਲ ਕਰੋ"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ਕੱਲ੍ਹ ਨੂੰ ਆਪਣੇ ਆਪ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ਕਵਿੱਕ ਸ਼ੇਅਰ, Find My Device ਅਤੇ ਡੀਵਾਈਸ ਦਾ ਟਿਕਾਣਾ ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਬਲੂਟੁੱਥ ਦੀ ਵਰਤੋਂ ਕਰਦੀਆਂ ਹਨ"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ਬਲੂਟੁੱਥ ਕੱਲ੍ਹ ਸਵੇਰੇ 5 ਵਜੇ ਚਾਲੂ ਹੋਵੇਗਾ"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string>
@@ -328,7 +336,7 @@
<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>
<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_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>
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"<xliff:g id="TIME">%s</xliff:g> ਵਜੇ ਚਾਲੂ"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"ਸਮੱਸਿਆ ਰਿਕਾਰਡ ਕਰੋ"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"ਸ਼ੁਰੂ ਕਰੋ"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"ਬੰਦ ਕਰੋ"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ਬੱਗ ਰਿਪੋਰਟ"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਕਿਹੜੀ ਸੁਵਿਧਾ ਪ੍ਰਭਾਵਿਤ ਹੋਈ ਸੀ?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ਸਮੱਸਿਆ ਦੀ ਕਿਸਮ ਚੁਣੋ"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ਮਿਆਰੀ"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"ਦਰਮਿਆਨਾ"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"ਜ਼ਿਆਦਾ"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"\'ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ\' \'ਤੇ ਕਲਿੱਕ ਕਰੋ"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"ਕੀ-ਬੋਰਡ ਖਾਕਾ ਬਦਲੋ"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ਜਾਂ"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ਖੋਜ ਪੁੱਛਗਿੱਛ ਕਲੀਅਰ ਕਰੋ"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ਸ਼ਾਰਟਕੱਟ"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ਸ਼ਾਰਟਕੱਟਾਂ ਨੂੰ ਖੋਜੋ"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਨਹੀਂ ਮਿਲਿਆ"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"ਸਿਸਟਮ"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant ਖੋਲ੍ਹੋ"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ਲਾਕ ਸਕ੍ਰੀਨ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"ਨੋਟ ਲਿਖੋ"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ਸਿਸਟਮ ਮਲਟੀਟਾਸਕਿੰਗ"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"RHS ਲਈ ਮੌਜੂਦਾ ਐਪ ਨਾਲ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"LHS ਲਈ ਮੌਜੂਦਾ ਐਪ ਨਾਲ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ਮਲਟੀਟਾਸਕਿੰਗ"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"ਸੱਜੇ ਪਾਸੇ ਦਿੱਤੀ ਮੌਜੂਦਾ ਐਪ ਨਾਲ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"ਖੱਬੇ ਪਾਸੇ ਦਿੱਤੀ ਮੌਜੂਦਾ ਐਪ ਨਾਲ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਤੋਂ ਪੂਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਸੱਜੇ ਜਾਂ ਹੇਠਾਂ ਮੌਜੂਦ ਐਪ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਖੱਬੇ ਜਾਂ ਉੱਪਰ ਮੌਜੂਦ ਐਪ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
index afb1e8b..62dc05a 100644
--- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"ਬੰਦ"</item>
<item msgid="5137565285664080143">"ਚਾਲੂ"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"ਉਪਲਬਧ ਨਹੀਂ"</item>
+ <item msgid="3079622119444911877">"ਬੰਦ ਹੈ"</item>
+ <item msgid="3028994095749238254">"ਚਾਲੂ ਹੈ"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 48262a0..814d321 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -270,12 +270,15 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Pokaż wszystkie"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Używaj Bluetootha"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Połączone"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Udostępnianie dźwięku"</string>
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Zapisane"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"rozłącz"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktywuj"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatycznie włącz ponownie jutro"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcje takie jak szybkie udostępnianie, Znajdź moje urządzenie czy lokalizacja urządzenia używają Bluetootha"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth włączy się jutro o 5 rano"</string>
+ <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcje takie jak szybkie udostępnianie czy Znajdź moje urządzenie korzystają z Bluetootha"</string>
+ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth włączy się jutro rano"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Udostępnianie dźwięku"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Udostępniam dźwięk"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> naładowania baterii"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string>
@@ -350,6 +353,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Zarejestruj problem"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Rozpocznij"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Zatrzymaj"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Raport o błędzie"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Którego aspektu korzystania z urządzenia dotyczył problem?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Wybierz typ problemu"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Nagrywanie ekranu"</string>
@@ -363,14 +367,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardowy"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Średni"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Wysoki"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Urządzenia słuchowe"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Urządzenia słuchowe"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sparuj nowe urządzenie"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknij, aby sparować nowe urządzenie"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Odblokować mikrofon urządzenia?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Odblokować aparat urządzenia?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Odblokować aparat i mikrofon urządzenia?"</string>
@@ -670,15 +670,15 @@
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez dźwięku i wibracji"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Brak dźwięku i wibracji, wyświetlają się niżej w sekcji rozmów"</string>
<string name="notification_channel_summary_default" msgid="777294388712200605">"Mogą włączać dzwonek lub wibracje w zależności od ustawień urządzenia"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Mogą włączyć dzwonek lub wibracje w zależności od ustawień urządzenia. Rozmowy z aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> są domyślnie wyświetlane jako dymki."</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Mogą włączać dzwonek lub wibracje w zależności od ustawień urządzenia. Rozmowy z aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> są domyślnie wyświetlane jako dymki."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Pozwól systemowi decydować, czy o powiadomieniu powinien informować dźwięk czy wibracja"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Stan:</b> zmieniony na Domyślny"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Stan:</b> zmieniono na Ciche"</string>
<string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>Stan:</b> podniesiono ważność"</string>
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>Stan:</b> obniżono ważność"</string>
- <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"Wyświetla się u góry powiadomień w rozmowach oraz jako zdjęcie profilowe na ekranie blokady"</string>
- <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"Wyświetla się u góry powiadomień w rozmowach oraz jako zdjęcie profilowe na ekranie blokady, jako dymek"</string>
- <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"Wyświetla się u góry powiadomień w rozmowach oraz jako zdjęcie profilowe na ekranie blokady, przerywa działanie trybu Nie przeszkadzać"</string>
+ <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"Wyświetlają się u góry powiadomień w rozmowach oraz jako zdjęcie profilowe na ekranie blokady"</string>
+ <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"Wyświetlają się u góry powiadomień w rozmowach oraz jako zdjęcie profilowe na ekranie blokady, jako dymek"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"Wyświetlają się u góry powiadomień w rozmowach oraz jako zdjęcie profilowe na ekranie blokady, przerywają działanie trybu Nie przeszkadzać"</string>
<string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Wyświetla się u góry powiadomień w rozmowach oraz jako zdjęcie profilowe na ekranie blokady, jako dymek, przerywa działanie trybu Nie przeszkadzać"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priorytetowe"</string>
<string name="no_shortcut" msgid="8257177117568230126">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> nie obsługuje funkcji rozmów"</string>
@@ -745,7 +745,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Przełącz układ klawiatury"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"lub"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Wyczyść wyszukiwanie hasło"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Skróty"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Skróty klawiszowe"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Wyszukiwanie skrótów"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nie znaleziono skrótów"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"System"</string>
@@ -757,29 +757,29 @@
<string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Pokazuję skróty dotyczące danych wejściowych"</string>
<string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Pokazuję skróty otwierające aplikacje"</string>
<string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Pokazuję skróty dotyczące bieżącej aplikacji"</string>
- <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Wyświetlanie powiadomień"</string>
- <string name="group_system_full_screenshot" msgid="5742204844232667785">"Robienie zrzutu ekranu"</string>
- <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Pokazywanie skrótów"</string>
- <string name="group_system_go_back" msgid="2730322046244918816">"Przechodzenie wstecz"</string>
- <string name="group_system_access_home_screen" msgid="4130366993484706483">"Wyświetlanie ekranu głównego"</string>
- <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Wyświetlanie ostatnich aplikacji"</string>
- <string name="group_system_cycle_forward" msgid="5478663965957647805">"Przełączanie się do przodu między ostatnimi aplikacjami"</string>
- <string name="group_system_cycle_back" msgid="8194102916946802902">"Przełączanie się wstecz między ostatnimi aplikacjami"</string>
- <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Otwieranie listy aplikacji"</string>
- <string name="group_system_access_system_settings" msgid="8731721963449070017">"Otwieranie ustawień"</string>
- <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otwieranie asystenta"</string>
- <string name="group_system_lock_screen" msgid="7391191300363416543">"Blokada ekranu"</string>
+ <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Wyświetl powiadomienia"</string>
+ <string name="group_system_full_screenshot" msgid="5742204844232667785">"Zapisz zrzut ekranu"</string>
+ <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Pokaż skróty"</string>
+ <string name="group_system_go_back" msgid="2730322046244918816">"Przejdź wstecz"</string>
+ <string name="group_system_access_home_screen" msgid="4130366993484706483">"Wyświetl ekran główny"</string>
+ <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Wyświetl ostatnie aplikacje"</string>
+ <string name="group_system_cycle_forward" msgid="5478663965957647805">"Przełącz się do przodu między ostatnimi aplikacjami"</string>
+ <string name="group_system_cycle_back" msgid="8194102916946802902">"Przełącz się wstecz między ostatnimi aplikacjami"</string>
+ <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Otwórz listę aplikacji"</string>
+ <string name="group_system_access_system_settings" msgid="8731721963449070017">"Otwórz ustawienia"</string>
+ <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otwórz asystenta"</string>
+ <string name="group_system_lock_screen" msgid="7391191300363416543">"Zablokuj ekran"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Zanotuj"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Wielozadaniowość w systemie"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Uruchamianie trybu podzielonego ekranu z bieżącą aplikacją po prawej"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Uruchamianie trybu podzielonego ekranu z bieżącą aplikacją po lewej"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Wielozadaniowość"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Podziel ekran z bieżącą aplikacją widoczną po prawej"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Podziel ekran z bieżącą aplikacją widoczną po lewej"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Przełącz podzielony ekran na pełny ekran"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Przełącz się na aplikację po prawej lub poniżej na podzielonym ekranie"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Przełącz się na aplikację po lewej lub powyżej na podzielonym ekranie"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Podczas podzielonego ekranu: zastępowanie aplikacji"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Wprowadzanie"</string>
- <string name="input_switch_input_language_next" msgid="3782155659868227855">"Przełączanie na następny język"</string>
- <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Przełączanie na poprzedni język"</string>
+ <string name="input_switch_input_language_next" msgid="3782155659868227855">"Przełącz na następny język"</string>
+ <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Przełącz na poprzedni język"</string>
<string name="input_access_emoji" msgid="8105642858900406351">"Otwieranie emotikonów"</string>
<string name="input_access_voice_typing" msgid="7291201476395326141">"Otwieranie pisania głosowego"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Aplikacje"</string>
diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
index 5d1c02e..5aa719f 100644
--- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Wyłączono"</item>
<item msgid="5137565285664080143">"Włączono"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Niedostępne"</item>
+ <item msgid="3079622119444911877">"Wyłączono"</item>
+ <item msgid="3028994095749238254">"Włączone"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 04bdf14..17fde25 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Mostrar tudo"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Recursos como o Quick Share, o Encontre Meu Dispositivo e a localização do dispositivo usam o Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"O Bluetooth será ativado amanhã às 5h"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Problema na gravação"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Relatório do bug"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Padrão"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Médio"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Alto"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparelhos auditivos"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmera do dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Desbloquear a câmera e o microfone do dispositivo?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Alterar layout do teclado"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Limpar a consulta de pesquisa"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Atalhos"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Atalhos do teclado"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Atalhos de pesquisa"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nenhum atalho encontrado"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string>
@@ -769,10 +774,10 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Abrir configurações"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir o Google Assistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Tela de bloqueio"</string>
- <string name="group_system_quick_memo" msgid="3764560265935722903">"Crie uma nota"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitarefa do sistema"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Usar a tela dividida com o app atual à direita"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Usar a tela dividida com o app atual à esquerda"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Criar nota"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarefas"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar a tela dividida com o aplicativo atual à direita"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar a tela dividida com o app atual à esquerda"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Mudar da tela dividida para a tela cheia"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mude para o app à direita ou abaixo ao usar a tela dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mude para o app à esquerda ou acima ao usar a tela dividida"</string>
@@ -830,7 +835,7 @@
<string name="left_icon" msgid="5036278531966897006">"Ícone à esquerda"</string>
<string name="right_icon" msgid="1103955040645237425">"Ícone à direita"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantenha pressionado e arraste para adicionar blocos"</string>
- <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Para reorganizar, toque no bloco sem soltar e arraste."</string>
+ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Para reorganizar, toque no bloco sem soltar e arraste"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arraste aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"É preciso haver pelo menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> blocos"</string>
<string name="qs_edit" msgid="5583565172803472437">"Editar"</string>
@@ -1297,7 +1302,7 @@
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"Em uso pela ligação telefônica"</string>
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"Usado recentemente em uma ligação telefônica"</string>
<string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Em uso pelo app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Usado recentemente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Uso recente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Em uso pelo 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 pelo 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 uso pelo 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>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Acesse rapidamente a automação residencial como um protetor de tela"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Controles de automação residencial no protetor de tela"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
index 453d813..3526c77 100644
--- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Desativado"</item>
<item msgid="5137565285664080143">"Ativado"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Indisponível"</item>
+ <item msgid="3079622119444911877">"Desativar"</item>
+ <item msgid="3028994095749238254">"Ativar"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index b627090..dfb7695 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver tudo"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ligado"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desassociar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Reativar amanhã automaticamente"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"As funcionalidades como Partilha rápida, Localizar o meu dispositivo e localização do dispositivo usam o Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"O Bluetooth vai ser ativado amanhã às 05:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Registar problema"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Relatório de erro"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da experiência do disposit. foi afetada?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecione o tipo de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de ecrã"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Médio"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Alto"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sincronizar novo dispositivo"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para sincronizar um novo dispositivo"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmara do dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Quer desbloquear a câmara e o microfone?"</string>
@@ -667,7 +673,7 @@
<string name="notification_alert_title" msgid="3656229781017543655">"Predefinição"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sem som ou vibração"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sem som ou vibração e aparece na parte inferior na secção de conversas."</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sem som ou vibração e aparece na parte inferior na secção de conversas"</string>
<string name="notification_channel_summary_default" msgid="777294388712200605">"Pode tocar ou vibrar com base nas definições do dispositivo"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Pode tocar ou vibrar com base nas definições do dispositivo. As conversas da app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem como um balão por predefinição."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se esta notificação deve emitir um som ou uma vibração"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Alterar esquema de teclado"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Limpar consulta de pesquisa"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Atalhos"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Atalhos de teclado"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pesquise atalhos"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nenhum atalho encontrado"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string>
@@ -768,12 +774,12 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Abrir definições"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir Assistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Ecrã de bloqueio"</string>
- <string name="group_system_quick_memo" msgid="3764560265935722903">"Tire uma nota"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Execução de várias tarefas em simultâneo no sistema"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Aceder ao ecrã dividido com a app atual para RHS"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Aceder ao ecrã dividido com a app atual para LHS"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Tire notas"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Execução de várias tarefas em simultâneo"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Use o ecrã dividido com a app atual à direita"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Use o ecrã dividido com a app atual à esquerda"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Mudar de ecrã dividido para ecrã inteiro"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mude para a app à direita ou abaixo enquanto usa o ecrã dividido"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para a app à direita ou abaixo enquanto usa o ecrã dividido"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mude para a app à esquerda ou acima enquanto usa o ecrã dividido"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Durante o ecrã dividido: substituir uma app por outra"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
@@ -798,7 +804,7 @@
<string name="accessibility_long_click_tile" msgid="210472753156768705">"Abrir as definições"</string>
<string name="accessibility_status_bar_headphones" msgid="1304082414912647414">"Auscultadores ligados"</string>
<string name="accessibility_status_bar_headset" msgid="2699275863720926104">"Auscultadores com microfone integrado ligados"</string>
- <string name="data_saver" msgid="3484013368530820763">"Poup. dados"</string>
+ <string name="data_saver" msgid="3484013368530820763">"Poupança de dados"</string>
<string name="accessibility_data_saver_on" msgid="5394743820189757731">"Poupança de dados ativada"</string>
<string name="switch_bar_on" msgid="1770868129120096114">"Ativado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desativado"</string>
@@ -1304,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controlos domésticos"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Aceda rapid. aos contr. domést. como prot. de ecrã"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Use controlos domésticos como proteção de ecrã"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 04bdf14..17fde25 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Mostrar tudo"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Recursos como o Quick Share, o Encontre Meu Dispositivo e a localização do dispositivo usam o Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"O Bluetooth será ativado amanhã às 5h"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Problema na gravação"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Relatório do bug"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Padrão"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Médio"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Alto"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparelhos auditivos"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmera do dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Desbloquear a câmera e o microfone do dispositivo?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Alterar layout do teclado"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Limpar a consulta de pesquisa"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Atalhos"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Atalhos do teclado"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Atalhos de pesquisa"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nenhum atalho encontrado"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string>
@@ -769,10 +774,10 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Abrir configurações"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir o Google Assistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Tela de bloqueio"</string>
- <string name="group_system_quick_memo" msgid="3764560265935722903">"Crie uma nota"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitarefa do sistema"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Usar a tela dividida com o app atual à direita"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Usar a tela dividida com o app atual à esquerda"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Criar nota"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarefas"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar a tela dividida com o aplicativo atual à direita"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar a tela dividida com o app atual à esquerda"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Mudar da tela dividida para a tela cheia"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mude para o app à direita ou abaixo ao usar a tela dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mude para o app à esquerda ou acima ao usar a tela dividida"</string>
@@ -830,7 +835,7 @@
<string name="left_icon" msgid="5036278531966897006">"Ícone à esquerda"</string>
<string name="right_icon" msgid="1103955040645237425">"Ícone à direita"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantenha pressionado e arraste para adicionar blocos"</string>
- <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Para reorganizar, toque no bloco sem soltar e arraste."</string>
+ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Para reorganizar, toque no bloco sem soltar e arraste"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arraste aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"É preciso haver pelo menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> blocos"</string>
<string name="qs_edit" msgid="5583565172803472437">"Editar"</string>
@@ -1297,7 +1302,7 @@
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"Em uso pela ligação telefônica"</string>
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"Usado recentemente em uma ligação telefônica"</string>
<string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Em uso pelo app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Usado recentemente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Uso recente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Em uso pelo 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 pelo 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 uso pelo 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>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Acesse rapidamente a automação residencial como um protetor de tela"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Controles de automação residencial no protetor de tela"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
index 453d813..3526c77 100644
--- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Desativado"</item>
<item msgid="5137565285664080143">"Ativado"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Indisponível"</item>
+ <item msgid="3079622119444911877">"Desativar"</item>
+ <item msgid="3028994095749238254">"Ativar"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 7d65f96..2af8013 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Afișează tot"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Folosește Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectat"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvat"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deconectează"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activează"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activează din nou automat mâine"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funcții precum Quick Share, Găsește-mi dispozitivul și locația dispozitivului folosesc Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth se va activa mâine la 5 dimineața"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Problemă legată de înregistrare"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Începe"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Oprește"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Raport de eroare"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ce parte a experienței pe dispozitiv a fost afectată?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selectează tipul problemei"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Înregistrarea ecranului"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Mediu"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Ridicat"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparate auditive"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparate auditive"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Asociază un nou dispozitiv"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Dă clic pentru a asocia un nou dispozitiv"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblochezi microfonul dispozitivului?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblochezi camera dispozitivului?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblochezi camera și microfonul dispozitivului?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Schimbă aspectul tastaturii"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"sau"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Șterge termenul de căutare"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Comenzi rapide"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Comenzi rapide de la tastatură"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Caută comenzi rapide"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nu există comenzi rapide"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistem"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Deschide Asistentul"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Ecranul de blocare"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Creează o notă"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking pe sistem"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Accesează ecranul împărțit cu aplicația actuală în dreapta"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Accesează ecranul împărțit cu aplicația actuală în stânga"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Folosește ecranul împărțit cu aplicația curentă în dreapta"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Folosește ecranul împărțit cu aplicația curentă în stânga"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Comută de la ecranul împărțit la ecranul complet"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Treci la aplicația din dreapta sau de mai jos cu ecranul împărțit"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Treci la aplicația din stânga sau de mai sus cu ecranul împărțit"</string>
diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
index 5b96618..a68f140 100644
--- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Dezactivat"</item>
<item msgid="5137565285664080143">"Activat"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Indisponibil"</item>
+ <item msgid="3079622119444911877">"Dezactivat"</item>
+ <item msgid="3028994095749238254">"Activat"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 5e47464..16fe331 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Все"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Использовать"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Подключено"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сохранено"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"отключить"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активировать"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Включить завтра автоматически"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Bluetooth используется в сервисе \"Найти устройство\", таких функциях, как Быстрая отправка, и при определении местоположения устройства"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth будет включен завтра в 05:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Запись неисправности"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Начать"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Остановить"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Отчет об ошибке"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"С чем связана проблема, с которой вы столкнулись?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Выберите тип проблемы"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запись экрана"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандартная"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Средняя"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Высокая"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слуховые аппараты"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слуховые аппараты"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Подключить новое устройство"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Нажмите, чтобы подключить новое устройство"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблокировать микрофон устройства?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблокировать камеру устройства?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблокировать камеру и микрофон устройства?"</string>
@@ -678,7 +683,7 @@
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>Статус:</b> уровень важности понижен"</string>
<string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"Появляется в верхней части уведомлений о сообщениях, а также в качестве фото профиля на заблокированном экране"</string>
<string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"Появляется в верхней части уведомлений о сообщениях, в виде всплывающего чата, а также в качестве фото профиля на заблокированном экране."</string>
- <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"Появляется в верхней части уведомлений о сообщениях, а также в качестве фото профиля на заблокированном экране, прерывает режим \"Не беспокоить\"."</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"Появляется в верхней части уведомлений о сообщениях, а также в виде фото профиля на заблокированном экране, прерывает режим \"Не беспокоить\"."</string>
<string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Появляется в верхней части уведомлений о сообщениях, в виде всплывающего чата, а также в качестве фото профиля на заблокированном экране, прерывает режим \"Не беспокоить\"."</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string>
<string name="no_shortcut" msgid="8257177117568230126">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" не поддерживает функции разговоров."</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Переключение раскладки"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"или"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Удалить поисковый запрос"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Сочетания клавиш"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Быстрые клавиши"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Поиск сочетаний клавиш"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Нет сочетаний клавиш."</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Система"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Открыть Ассистента"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Заблокировать экран"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Создать заметку"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Режим многозадачности"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Включить разделение экрана с текущим приложением справа"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Включить разделение экрана с текущим приложением слева"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Многозадачность"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Разделить экран и поместить это приложение справа"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Разделить экран и поместить это приложение слева"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Изменить режим разделения экрана на полноэкранный режим"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Перейти к приложению справа или внизу на разделенном экране"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Перейти к приложению слева или вверху на разделенном экране"</string>
@@ -783,7 +788,7 @@
<string name="input_access_emoji" msgid="8105642858900406351">"Открыть список эмодзи"</string>
<string name="input_access_voice_typing" msgid="7291201476395326141">"Активировать голосовой ввод"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Приложения"</string>
- <string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"Открыть Ассистента"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"Ассистент"</string>
<string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Браузер"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"Контакты"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"Эл. почта"</string>
@@ -1123,8 +1128,8 @@
<string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер сборки"</string>
<string name="build_number_copy_toast" msgid="877720921605503046">"Номер сборки скопирован в буфер обмена."</string>
<string name="basic_status" msgid="2315371112182658176">"Открытый чат"</string>
- <string name="select_conversation_title" msgid="6716364118095089519">"Виджеты чатов"</string>
- <string name="select_conversation_text" msgid="3376048251434956013">"Нажмите на чат, чтобы добавить его на главный экран"</string>
+ <string name="select_conversation_title" msgid="6716364118095089519">"Виджеты разговоров"</string>
+ <string name="select_conversation_text" msgid="3376048251434956013">"Нажмите на разговор, чтобы добавить его на главный экран"</string>
<string name="no_conversations_text" msgid="5354115541282395015">"Здесь появятся ваши недавние разговоры."</string>
<string name="priority_conversations" msgid="3967482288896653039">"Важные разговоры"</string>
<string name="recent_conversations" msgid="8531874684782574622">"Недавние разговоры"</string>
@@ -1291,7 +1296,7 @@
<string name="privacy_dialog_expand_action" msgid="9129262348628331377">"Развернуть и показать параметры"</string>
<string name="privacy_dialog_collapse_action" msgid="277419962019466347">"Свернуть"</string>
<string name="privacy_dialog_close_app_button" msgid="8006250171305878606">"Закрыть это приложение"</string>
- <string name="privacy_dialog_close_app_message" msgid="1316408652526310985">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" закрыто."</string>
+ <string name="privacy_dialog_close_app_message" msgid="1316408652526310985">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" закрыто"</string>
<string name="privacy_dialog_manage_service" msgid="8320590856621823604">"Настроить сервис"</string>
<string name="privacy_dialog_manage_permissions" msgid="2543451567190470413">"Настроить доступ"</string>
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"Сейчас используется для телефонного звонка"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка клавиатуры"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Уровень %1$d из %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Управление домом"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Добавьте настройки умного дома на заставку"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Быстрый доступ к управлению домом через заставку"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
index cdc4a98..592937c 100644
--- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Выключено"</item>
<item msgid="5137565285664080143">"Включено"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Недоступны"</item>
+ <item msgid="3079622119444911877">"Отключены"</item>
+ <item msgid="3028994095749238254">"Включены"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 5eea02c..458d9ee 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"සියල්ල බලන්න"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"බ්ලූටූත් භාවිතා කරන්න"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"සම්බන්ධිතයි"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"සුරැකිණි"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"විසන්ධි කරන්න"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"සක්රිය කරන්න"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"හෙට ස්වයංක්රීයව නැවත ක්රියාත්මක කරන්න"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ඉක්මන් බෙදා ගැනීම, මගේ උපාංගය සෙවීම, සහ උපාංග ස්ථානය වැනි විශේෂාංග බ්ලූටූත් භාවිත කරයි"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"බ්ලූටූත් හෙට පෙ.ව. 5ට ක්රියාත්මක වනු ඇත"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්රව්ය"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"පටිගත කිරීමේ ගැටලුව"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"අරඹන්න"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"නවත්වන්න"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"දෝෂ වර්තාව"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ඔබේ උපාංග අත්දැකීමේ කුමන කොටසට බලපෑවේ ද?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ගැටලු වර්ගය තෝරන්න"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"තිර පටිගත කිරීම"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"සම්මත"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"මධ්යම"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"ඉහළ"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ශ්රවණ උපාංග"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ශ්රවණ උපාංග"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"නව උපාංගය යුගල කරන්න"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"නව උපාංගය යුගල කිරීමට ක්ලික් කරන්න"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"උපාංග මයික්රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"උපාංග කැමරාව අවහිර කිරීම ඉවත් කරන්නද?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"උපාංග කැමරාව සහ මයික්රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"යතුරුපුවරු පිරිසැලසුම මාරු කරන්න"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"හෝ"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"සෙවීම් විමසුම හිස් කරන්න"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"කෙටිමං"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"යතුරු පුවරු කෙටිමං"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"කෙටිමං සොයන්න"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"කෙටිමං හමු නොවුණි"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"පද්ධතිය"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"සහායක විවෘත කරන්න"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"තිරය අගුළු දමන්න"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"සටහනක් ගන්න"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"පද්ධති බහු කාර්ය"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"RHS වෙත වත්මන් යෙදුම සමග බෙදුම් තිරයට ඇතුළු වන්න"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"LHS වෙත වත්මන් යෙදුම සමග බෙදුම් තිරයට ඇතුළු වන්න"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"බහුකාර්ය"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"දකුණේ වත්මන් යෙදුම සමග බෙදීම් තිරය භාවිතා කරන්න"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"වම් පැත්තේ වත්මන් යෙදුම සමග බෙදීම් තිරය භාවිතා කරන්න"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"බෙදුම් තිරයේ සිට පූර්ණ තිරයට මාරු වන්න"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"බෙදුම් තිරය භාවිත කරන අතරතුර දකුණේ හෝ පහළින් ඇති යෙදුමට මාරු වන්න"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"බෙදුම් තිරය භාවිත කරන අතරතුර වමේ හෝ ඉහළ ඇති යෙදුමට මාරු වන්න"</string>
diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml
index e7e9034..681f3d5 100644
--- a/packages/SystemUI/res/values-si/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"ක්රියාවිරහිතයි"</item>
<item msgid="5137565285664080143">"ක්රියාත්මකයි"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"නොමැත"</item>
+ <item msgid="3079622119444911877">"ක්රියාවිරහිතයි"</item>
+ <item msgid="3028994095749238254">"ක්රියාත්මකයි"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index ea1a8f8..c8c2ee5 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Zobraziť všetko"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Použiť Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Pripojené"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uložené"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojiť"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovať"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automaticky zajtra znova zapnúť"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcie, ako sú Quick Share, Nájdi moje zariadenie a poloha zariadenia, používajú Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth sa zapne zajtra o 5:00."</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Problém s nahrávaním"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Začnite"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Zastavte"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Hlásenie chyby"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Čo v zariadení bolo ovplyvnené?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte typ problému"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekordér obrazovky"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Stredný"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Vysoký"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Načúvacie zariadenia"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Načúvacie zariadenia"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Párovanie nového zariadenia"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zariadenie"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Chcete odblokovať mikrofón zariadenia?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Chcete odblokovať kameru zariadenia?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Chcete odblokovať fotoaparát a mikrofón zariadenia?"</string>
@@ -679,7 +685,7 @@
<string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"Zobrazuje sa ako bublina v hornej časti upozornení konverzácie a profilová fotka na uzamknutej obrazovke"</string>
<string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"Zobrazuje sa v hornej časti upozornení konverzácie a ako profilová fotka na uzamknutej obrazovke, preruší režim bez vyrušení"</string>
<string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Zobrazuje sa ako bublina v hornej časti upozornení konverzácie a profilová fotka na uzamknutej obrazovke, preruší režim bez vyrušení"</string>
- <string name="notification_priority_title" msgid="2079708866333537093">"Priorita"</string>
+ <string name="notification_priority_title" msgid="2079708866333537093">"Prioritné"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> nepodporuje funkcie konverzácie"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"Tieto upozornenia sa nedajú upraviť."</string>
<string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Upozornenia na hovory sa nedajú upraviť."</string>
@@ -744,12 +750,12 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Prepnúť rozloženie klávesnice"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"alebo"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Vymazať vyhľadávací dopyt"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Skratky"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Klávesové skratky"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Hľadajte skratky"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nenašli sa žiadne skratky"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Systém"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Vstup"</string>
- <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Otvorenie apl."</string>
+ <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Otvorené aplikácie"</string>
<string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Aktuálna aplik."</string>
<string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Zobrazujú sa výsledky vyhľadávania"</string>
<string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Zobrazujú sa skratky systému"</string>
@@ -767,11 +773,11 @@
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Otvorenie zoznamu aplikácií"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvorenie nastavení"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvorenie Asistenta"</string>
- <string name="group_system_lock_screen" msgid="7391191300363416543">"Zamknúť obrazovku"</string>
+ <string name="group_system_lock_screen" msgid="7391191300363416543">"Uzamknutie obrazovky"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Napísanie poznámky"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking systému"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Rozdelenie obrazovky s aktuálnou aplikáciou vpravo"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Rozdelenie obrazovky s aktuálnou aplikáciou vľavo"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Použite rozdelenú obrazovku s aktuálnou aplikáciou vpravo"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Použite rozdelenú obrazovku s aktuálnou aplikáciou vľavo"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Prepnutie rozdelenej obrazovky na celú"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prechod na aplikáciu vpravo alebo dole pri rozdelenej obrazovke"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Prechod na aplikáciu vľavo alebo hore pri rozdelenej obrazovke"</string>
@@ -1268,7 +1274,7 @@
<string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"Inštalovať pracovnú telefónnu aplikáciu"</string>
<string name="call_from_work_profile_close" msgid="5830072964434474143">"Zrušiť"</string>
<string name="lock_screen_settings" msgid="6152703934761402399">"Prispôsobiť uzamknutú obrazovku"</string>
- <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Ak chcete prispôsobiť uzamknutú obrazovku, odomknite ju"</string>
+ <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Uzamknutú obrazovku môžete prispôsobiť po odomknutí"</string>
<string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi‑Fi nie je k dispozícii"</string>
<string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera je blokovaná"</string>
<string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera a mikrofón sú blokované"</string>
@@ -1284,7 +1290,7 @@
<string name="dismiss_dialog" msgid="2195508495854675882">"Zavrieť"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Obrazovka je pripojená"</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_summary" msgid="2458769652125995409">"Nedávne použitie aplikáciami"</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>
<string name="privacy_dialog_expand_action" msgid="9129262348628331377">"Rozbaliť a zobraziť možnosti"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 514d2f9..0302199 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Pokaži vse"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Uporabi Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Shranjeno"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinitev povezave"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Samodejno znova vklopi jutri"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcije, kot so Hitro deljenje, Poišči mojo napravo in zaznavanje lokacije naprave, uporabljajo Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth se bo vklopil jutri ob 5. uri"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Snemanje težave"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Začetek"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Ustavitev"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Poročilo o napakah"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na kateri del izkušnje z napravo je to vplivalo?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izberite vrsto težave"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snemanje zaslona"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardni"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Srednji"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Visok"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni pripomočki"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni pripomočki"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Seznanitev nove naprave"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite za seznanitev nove naprave"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite odblokirati mikrofon v napravi?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite odblokirati fotoaparat v napravi?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite odblokirati fotoaparat in mikrofon v napravi?"</string>
@@ -745,12 +750,12 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Preklop postavitve tipkovnice"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ali"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Čiščenje iskalne poizvedbe"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Bližnjice"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Bližnjične tipke"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Iskanje bližnjic"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ni najdenih bližnjic."</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistem"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Vnos"</string>
- <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Odprte aplikacije"</string>
+ <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Odpiranje aplikacij"</string>
<string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Trenutna aplikacija"</string>
<string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Prikaz rezultatov iskanja"</string>
<string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Prikaz sistemskih bližnjic"</string>
@@ -770,14 +775,14 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Odpiranje Pomočnika"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zaklepanje zaslona"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Ustvarjanje zapiska"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Sistemska večopravilnost"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Vklop razdeljenega zaslona s trenutno aplikacijo na desni"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Vklop razdeljenega zaslona s trenutno aplikacijo na levi"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Večopravilnost"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Uporaba razdeljenega zaslona s trenutno aplikacijo na desni"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Uporaba razdeljenega zaslona s trenutno aplikacijo na levi"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Preklop iz razdeljenega zaslona v celozaslonski način"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Preklop na aplikacijo desno ali spodaj med uporabo razdeljenega zaslona"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Preklop na aplikacijo levo ali zgoraj med uporabo razdeljenega zaslona"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Pri razdeljenem zaslonu: medsebojna zamenjava aplikacij"</string>
- <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vnosna naprava"</string>
+ <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vnos"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Preklop na naslednji jezik"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Preklop na prejšnji jezik"</string>
<string name="input_access_emoji" msgid="8105642858900406351">"Dostop do emodžijev"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Osvetlitev tipkovnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Stopnja %1$d od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrolniki za dom"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Hiter dostop do kontrol. za dom na ohranj. zaslona"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Hiter dostop do kontrolnikov za dom na ohranjevalniku zaslona"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
index 9f9175e..5f60ffd 100644
--- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Izklopljeno"</item>
<item msgid="5137565285664080143">"Vklopljeno"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Ni na voljo"</item>
+ <item msgid="3079622119444911877">"Izklopljeno"</item>
+ <item msgid="3028994095749238254">"Vklopljeno"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index c13d76b..8e621ce 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Shiko të gjitha"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Përdor Bluetooth-in"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Lidhur"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ruajtur"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"shkëput"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizo"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivizoje automatikisht nesër"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Veçoritë si \"Ndarja e shpejtë\", \"Gjej pajisjen time\" dhe vendndodhja e pajisjes përdorin Bluetooth-in"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth-i do të aktivizohet nesër në 5:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Regjistro problemin"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Nis"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Ndalo"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Raporti i defekteve në kod"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cila pjesë e përvojës me pajisjen është prekur?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Zgjidh llojin e problemit"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regjistrim i ekranit"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Mesatar"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"I lartë"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Pajisje ndihmëse për dëgjimin"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Pajisjet e dëgjimit"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Çifto pajisje të re"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliko për të çiftuar një pajisje të re"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Të zhbllokohet mikrofoni i pajisjes?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Të zhbllokohet kamera e pajisjes?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Të zhbllokohen kamera dhe mikrofoni i pajisjes?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Ndërro strukturën e tastierës"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ose"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Pastro pyetjen e kërkimit"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Shkurtoret"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Shkurtoret e tastierës"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Kërko shkurtoret"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nuk u gjet shkurtore"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistemi"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Hap \"Asistentin\""</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Ekrani i kyçjes"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Mbaj një shënim"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Kryerja e shumë detyrave nga sistemi"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Hyr në ekranin e ndarë me aplikacionin aktual në anën e djathtë"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Hyr në ekranin e ndarë me aplikacionin aktual në anën e majtë"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Kryerja e shumë detyrave"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Përdor ekranin e ndarë me aplikacionin aktual në të djathtë"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Përdor ekranin e ndarë me aplikacionin aktual në të majtë"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Kalo nga ekrani i ndarë në ekranin e plotë"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Kalo tek aplikacioni djathtas ose poshtë kur përdor ekranin e ndarë"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Kalo tek aplikacioni në të majtë ose sipër kur përdor ekranin e ndarë"</string>
diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
index aa4832d..9b5032e 100644
--- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Joaktive"</item>
<item msgid="5137565285664080143">"Aktive"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Nuk ofrohen"</item>
+ <item msgid="3079622119444911877">"Joaktive"</item>
+ <item msgid="3028994095749238254">"Aktive"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index bc6642a..a1825bf 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Прикажи све"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Користи Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Повезано"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сачувано"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекините везу"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирајте"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аутоматски поново укључи сутра"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Функције као што су Quick Share, Пронађи мој уређај и локација уређаја користе Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ће се укључити сутра у 5:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалице"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Евидентирајте проблем"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Покрени"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Заустави"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Извештај о грешци"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"На који део доживљаја на уређају је ово утицало?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изаберите тип проблема"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Снимање екрана"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Средње"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Високо"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Упари нови уређај"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликните да бисте упарили нов уређај"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Желите да одблокирате микрофон уређаја?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Желите да одблокирате камеру уређаја?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Желите да одблокирате камеру и микрофон уређаја?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Промени распоред тастатуре"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"или"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Обриши упит за претрагу"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Пречице"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Тастерске пречице"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Претражите пречице"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Нису пронађене пречице"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Систем"</string>
@@ -768,10 +774,10 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Отвори подешавања"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Отвори помоћника"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Закључавање екрана"</string>
- <string name="group_system_quick_memo" msgid="3764560265935722903">"Направите белешку"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Обављање више задатака система истовремено"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Покрени подељени екран за актуелну апликацију на десној страни"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Покрени подељени екран за актуелну апликацију на левој страни"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Направи белешку"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Обављање више задатака истовремено"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Користите подељени екран са актуелном апликацијом с десне стране"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Користите подељени екран са актуелном апликацијом с леве стране"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Пређи са подељеног екрана на цео екран"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Пређите у апликацију здесна или испод док користите подељени екран"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пређите у апликацију слева или изнад док користите подељени екран"</string>
@@ -1295,9 +1301,9 @@
<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_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_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>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 15c370c..abb5d09 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Se alla"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Använd Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ansluten"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sparad"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koppla från"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivera"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivera automatiskt igen i morgon"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funktioner som Snabbdelning, Hitta min enhet och enhetens plats använder Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth aktiveras i morgon kl. 5.00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Registrera problem"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Starta"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppa"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Felrapport"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Vilken enhetsupplevelse påverkades?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Välj problemtyp"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skärminspelning"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Medelhög"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Hög"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörhjälpmedel"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hörhjälpmedel"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parkoppla en ny enhet"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicka för att parkoppla en ny enhet"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vill du återaktivera enhetens mikrofon?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vill du återaktivera enhetens kamera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vill du återaktivera enhetens kamera och mikrofon?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Byt tangentbordslayout"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"eller"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Rensa sökfråga"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Kortkommandon"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Kortkommandon"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Sök efter kortkommando"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Inga resultat"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"System"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Öppna assistenten"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lås skärmen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Anteckna"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Systemets multikörning"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Öppna delad skärm med aktuell app till höger"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Öppna delad skärm med aktuell app till vänster"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multikörning"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Anänd delad skärm med den aktuella appen till höger"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Använd delad skärm med den aktuella appen till vänster"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Byt mellan delad skärm och helskärm"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Byt till appen till vänster eller nedanför när du använder delad skärm"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Byt till appen till vänster eller ovanför när du använder delad skärm"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrundsbelysning för tangentbord"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Hemstyrning"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Kom snabbt åt hemstyrningen som en skärmsläckare"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Kom snabbt åt hemstyrningen via skärmsläckaren"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
index 522538a..cf49f8d 100644
--- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Av"</item>
<item msgid="5137565285664080143">"På"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Inte tillgänglig"</item>
+ <item msgid="3079622119444911877">"Av"</item>
+ <item msgid="3028994095749238254">"På"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 41bac7b..392a74d 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Angalia vyote"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Tumia Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Imeunganishwa"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Imehifadhiwa"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ondoa"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"anza kutumia"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Iwashe tena kesho kiotomatiki"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Vipengele kama vile Kutuma Haraka, Tafuta Kifaa Changu na mahali kifaa kilipo hutumia Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth itawaka kesho saa 11 alfajiri"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Hitilafu ya Kurekodi"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Anza"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Simamisha"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Ripoti ya Hitilafu"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ni sehemu gani ya matumizi ya kifaa iliathiriwa?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chagua aina ya tatizo"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekodi ya skrini"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Wastani"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Juu"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Vifaa vya kusikilizia"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Vifaa vya kusikilizia"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Unganisha kifaa kipya"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Bofya ili uunganishe kifaa kipya"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Ungependa kuwacha kuzuia maikrofoni ya kifaa?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Ungependa kuacha kuzuia kamera ya kifaa?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Ungependa kuwacha kuzuia kamera na maikrofoni ya kifaa?"</string>
@@ -636,7 +642,7 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Fungua ili utumie"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Hitilafu imetokea wakati wa kuleta kadi zako, tafadhali jaribu tena baadaye"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Mipangilio ya kufunga skrini"</string>
- <string name="qr_code_scanner_title" msgid="1938155688725760702">"Kichanganuzi cha msimbo wa QR"</string>
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Kichanganuzi cha QR"</string>
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Inasasisha"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Wasifu wa kazini"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hali ya ndegeni"</string>
@@ -719,7 +725,7 @@
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
- <string name="keyboard_key_backspace" msgid="4095278312039628074">"Nafasinyuma"</string>
+ <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Cheza/Sitisha"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"Simamisha"</string>
<string name="keyboard_key_media_next" msgid="8502476691227914952">"Inayofuata"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Badili mkao wa kibodi"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"au"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Futa hoja ya utafutaji"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Njia za mkato"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Mikato ya Kibodi"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Tafuta njia za mkato"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Hakuna njia za mkato zilizopatikana"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Mfumo"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Fungua programu ya Mratibu wa Google"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Funga skrini"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Andika dokezo"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Majukumu mengi ya mfumo"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Tumia programu kwenye skrini iliyogawanywa upande wa kulia"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Tumia programu kwenye skrini iliyogawanywa upande wa kushoto"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Majukumu mengi"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Tumia hali ya kugawa skrini na programu ya sasa iwe upande wa kulia"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Tumia hali ya kugawa skrini na programu ya sasa iwe upande wa kushoto"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Badilisha kutoka skrini iliyogawanywa utumie skrini nzima"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Badilisha ili uende kwenye programu iliyo kulia au chini unapotumia hali ya kugawa skrini"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Badilisha uende kwenye programu iliyo kushoto au juu unapotumia hali ya kugawa skrini"</string>
@@ -1261,7 +1267,7 @@
<string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Chaji ya betri imesalia <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Unganisha stylus yako kwenye chaja"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Chaji ya betri ya Stylus imepungua"</string>
- <string name="video_camera" msgid="7654002575156149298">"Kamera ya kuchukulia video"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Kamera ya video"</string>
<string name="call_from_work_profile_title" msgid="5418253516453177114">"Huwezi kupiga simu kwa kutumia programu ya binafsi"</string>
<string name="call_from_work_profile_text" msgid="2856337395968118274">"Shirika lako linakuruhusu upige simu ukitumia programu za kazini pekee"</string>
<string name="call_from_work_profile_action" msgid="2937701298133010724">"Tumia wasifu wa kazini"</string>
@@ -1303,6 +1309,6 @@
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Ilitumiwa hivi majuzi na <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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string>
- <string name="home_controls_dream_label" msgid="6567105701292324257">"Vidhibiti vya Vifaa Nyumbani"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Fikia haraka vidhibiti vya vifaa nyumbani kama taswira ya skrini"</string>
+ <string name="home_controls_dream_label" msgid="6567105701292324257">"Dhibiti Vifaa Nyumbani"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Fikia haraka vidhibiti vya vifaa nyumbani vikiwa taswira ya skrini"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 019dddc..dfc00df 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"அனைத்தையும் காட்டு"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"புளூடூத்தைப் பயன்படுத்துதல்"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"இணைக்கப்பட்டது"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"சேமிக்கப்பட்டது"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"இணைப்பு நீக்கும்"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"செயல்படுத்தும்"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"நாளைக்குத் தானாகவே மீண்டும் இயக்கப்படும்"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"விரைவுப் பகிர்தல், Find My Device போன்ற அம்சங்களும் சாதன இருப்பிடமும் புளூடூத்தைப் பயன்படுத்துகின்றன"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"நாளை 5 AMக்கு புளூடூத் ஆன் ஆகும்"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> பேட்டரி"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"சிக்கலை ரெக்கார்டு செய்தல்"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"தொடங்குங்கள்"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"நிறுத்துங்கள்"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"பிழை அறிக்கை"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"சாதன அனுபவத்தின் எந்தப் பகுதி பாதிக்கப்பட்டது?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"சிக்கல் வகையைத் தேர்வுசெய்க"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ஸ்கிரீன் ரெக்கார்டு"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"இயல்புநிலை"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"நடுத்தரம்"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"அதிகம்"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"செவித்துணைக் கருவிகள்"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"செவித்துணைக் கருவிகள்"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"புதிய சாதனத்தை இணை"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"புதிய சாதனத்தை இணைக்க கிளிக் செய்யலாம்"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"சாதனத்தின் மைக்ரோஃபோனுக்கான தடுப்பை நீக்கவா?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"சாதனத்தின் கேமராவுக்கான தடுப்பை நீக்கவா?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"சாதனத்தின் கேமராவுக்கும் மைக்ரோஃபோனுக்குமான தடுப்பை நீக்கவா?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"கீபோர்டு லே அவுட்டை மாற்று"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"அல்லது"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"தேடல் வினவலை அழிக்கும்"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ஷார்ட்கட்கள்"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"கீபோர்டு ஷார்ட்கட்கள்"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ஷார்ட்கட்களைத் தேடுக"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ஷார்ட்கட்கள் எதுவுமில்லை"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"சிஸ்டம்"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistantடைத் திறத்தல்"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"பூட்டுத் திரை"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"குறிப்பெடுத்தல்"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"சிஸ்டம் பல வேலைகளைச் செய்தல்"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"வலதுபுறத்தில் தற்போதைய ஆப்ஸ் தோன்றுமாறு திரைப் பிரிப்பை அமைத்தல்"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"இடதுபுறத்தில் தற்போதைய ஆப்ஸ் தோன்றுமாறு திரைப் பிரிப்பை அமைத்தல்"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"பல வேலைகளைச் செய்தல்"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"தற்போது உள்ள ஆப்ஸுடன் வலதுபுறத்தில் திரைப் பிரிப்பைப் பயன்படுத்துதல்"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"தற்போது உள்ள ஆப்ஸுடன் இடதுபுறத்தில் திரைப் பிரிப்பைப் பயன்படுத்துதல்"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"திரைப் பிரிப்பு பயன்முறையிலிருந்து முழுத்திரைக்கு மாற்றுதல்"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது வலது/கீழ் உள்ள ஆப்ஸுக்கு மாறுங்கள்"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது இடது/மேலே உள்ள ஆப்ஸுக்கு மாறுங்கள்"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"கீபோர்டு பேக்லைட்"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"நிலை, %2$d இல் %1$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ஹோம் கன்ட்ரோல்கள்"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"ஹோம் கன்ட்ரோல்களை ஸ்கிரீன் சேவராக விரைவாக அணுகலாம்"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"ஹோம் கன்ட்ரோல்களை ஸ்கிரீன் சேவராக அணுகலாம்"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
index cacde5e..a3b9538 100644
--- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"முடக்கப்பட்டுள்ளது"</item>
<item msgid="5137565285664080143">"இயக்கப்பட்டுள்ளது"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"கிடைக்கவில்லை"</item>
+ <item msgid="3079622119444911877">"முடக்கப்பட்டுள்ளது"</item>
+ <item msgid="3028994095749238254">"இயக்கப்பட்டுள்ளது"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 1b3c5e2..518883f 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -118,7 +118,7 @@
<string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"స్క్రీన్, ఆడియో రికార్డింగ్ చేయబడుతున్నాయి"</string>
<string name="screenrecord_taps_label" msgid="1595690528298857649">"స్క్రీన్పై తాకే స్థానాలను చూపండి"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"ఆపివేయి"</string>
- <string name="screenrecord_share_label" msgid="5025590804030086930">"షేర్ చేయి"</string>
+ <string name="screenrecord_share_label" msgid="5025590804030086930">"షేర్ చేయండి"</string>
<string name="screenrecord_save_title" msgid="1886652605520893850">"స్క్రీన్ రికార్డింగ్ సేవ్ చేయబడింది"</string>
<string name="screenrecord_save_text" msgid="3008973099800840163">"చూడటానికి ట్యాప్ చేయండి"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"స్క్రీన్ రికార్డింగ్ను సేవ్ చేయడంలో ఎర్రర్ ఏర్పడింది"</string>
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"అన్నింటినీ చూడండి"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"బ్లూటూత్ వాడండి"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"కనెక్ట్ అయింది"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"సేవ్ చేయబడింది"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"డిస్కనెక్ట్ చేయండి"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"యాక్టివేట్ చేయండి"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"రేపు మళ్లీ ఆటోమేటిక్గా ఆన్ చేస్తుంది"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"క్విక్ షేర్, Find My Device, పరికర లొకేషన్ వంటి ఫీచర్లు బ్లూటూత్ను ఉపయోగిస్తాయి"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"బ్లూటూత్ రేపు ఉదయం 5 గంటలకు ఆన్ అవుతుంది"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> బ్యాటరీ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్సెట్"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"సమస్యను రికార్డ్ చేయడం"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"ప్రారంభించండి"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"ఆపివేయండి"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"బగ్ రిపోర్ట్"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"మీ పరికర అనుభవంలో ఏ భాగం ప్రభావితమైంది?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"సమస్య రకాన్ని ఎంచుకోండి"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"స్క్రీన్ రికార్డ్"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"స్టాండర్డ్"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"మధ్యస్థం"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"అధికం"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"వినికిడి పరికరాలు"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"వినికిడి పరికరం"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"కొత్త పరికరాన్ని పెయిర్ చేయడానికి క్లిక్ చేయండి"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"పరికరం మైక్రోఫోన్ను అన్బ్లాక్ చేయమంటారా?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"పరికరంలోని కెమెరాను అన్బ్లాక్ చేయమంటారా?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"పరికరంలోని కెమెరా, మైక్రోఫోన్లను అన్బ్లాక్ చేయమంటారా?"</string>
@@ -678,7 +683,7 @@
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>స్టేటస్:</b> తక్కువ ర్యాంక్కు సర్దుబాటు చేయబడింది"</string>
<string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"సంభాషణ నోటిఫికేషన్ల ఎగువున, లాక్ స్క్రీన్లో ప్రొఫైల్ ఫోటోగా చూపిస్తుంది"</string>
<string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"సంభాషణ నోటిఫికేషన్ల ఎగువున, లాక్ స్క్రీన్లో ప్రొఫైల్ ఫోటోగా చూపిస్తుంది, బబుల్గా కనిపిస్తుంది"</string>
- <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"సంభాషణ నోటిఫికేషన్ల ఎగువున, లాక్ స్క్రీన్లో ప్రొఫైల్ ఫోటోగా చూపిస్తుంది, \'అంతరాయం కలిగించవద్దు\'ను అంతరాయం కలిగిస్తుంది"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"సంభాషణ నోటిఫికేషన్ల ఎగువున, అలాగే లాక్ స్క్రీన్లో ప్రొఫైల్ ఫోటో రూపంలో కనబడుతుంది. \'అంతరాయం కలిగించవద్దు\' మోడ్లో ఉన్నా దాన్ని అధిగమిస్తుంది"</string>
<string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"సంభాషణ నోటిఫికేషన్ల ఎగువున, లాక్ స్క్రీన్లో ప్రొఫైల్ ఫోటోగా చూపిస్తుంది, బబుల్గా కనిపిస్తుంది, \'అంతరాయం కలిగించవద్దు\'ను అంతరాయం కలిగిస్తుంది"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ప్రాధాన్యత"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> సంభాషణ ఫీచర్లను సపోర్ట్ చేయదు"</string>
@@ -718,7 +723,7 @@
<string name="keyboard_key_dpad_right" msgid="8485763312139820037">"కుడి వైపు బాణం"</string>
<string name="keyboard_key_dpad_center" msgid="4079412840715672825">"మధ్య"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
- <string name="keyboard_key_space" msgid="6980847564173394012">"అంతరం"</string>
+ <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"ప్లే చేయి/పాజ్ చేయి"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"కీబోర్డ్ లేఅవుట్ను మార్చండి"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"లేదా"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"సెర్చ్ క్వెరీని క్లియర్ చేయండి"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"షార్ట్కట్లు"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"కీబోర్డ్ షార్ట్కట్లు"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"షార్ట్కట్స్ సెర్చ్ చేయండి"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"షార్ట్కట్లు ఏవీ లేవు"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"సిస్టమ్"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"అసిస్టెంట్ను తెరవండి"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"లాక్ స్క్రీన్"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"గమనికను రాయండి"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"సిస్టమ్ మల్టీ-టాస్కింగ్"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"RHSకు ప్రస్తుత యాప్తో స్ప్లిట్ స్క్రీన్ను ఎంటర్ చేయండి"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"LHSకు ప్రస్తుత యాప్తో స్ప్లిట్ స్క్రీన్ను ఎంటర్ చేయండి"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"మల్టీ-టాస్కింగ్"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"కుడివైపు ప్రస్తుత యాప్తో స్ప్లిట్ స్క్రీన్ను ఉపయోగించండి"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"ఎడమవైపు ప్రస్తుత యాప్తో స్ప్లిట్ స్క్రీన్ను ఉపయోగించండి"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"స్ప్లిట్ స్క్రీన్ను ఫుల్ స్క్రీన్కు మార్చండి"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు కుడి లేదా పైన యాప్నకు మారండి"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు ఎడమ లేదా పైన యాప్నకు మారండి"</string>
@@ -802,7 +807,7 @@
<string name="data_saver" msgid="3484013368530820763">"డేటా సేవర్"</string>
<string name="accessibility_data_saver_on" msgid="5394743820189757731">"డేటా సేవర్ ఆన్లో ఉంది"</string>
<string name="switch_bar_on" msgid="1770868129120096114">"ఆన్"</string>
- <string name="switch_bar_off" msgid="5669805115416379556">"ఆఫ్ చేయి"</string>
+ <string name="switch_bar_off" msgid="5669805115416379556">"ఆఫ్"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"అందుబాటులో లేదు"</string>
<string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"మరింత తెలుసుకోండి"</string>
<string name="nav_bar" msgid="4642708685386136807">"నావిగేషన్ బార్"</string>
@@ -1066,7 +1071,7 @@
<string name="controls_media_button_next" msgid="6662636627525947610">"తర్వాతి ట్రాక్"</string>
<string name="controls_media_button_connecting" msgid="3138354625847598095">"కనెక్ట్ అవుతోంది"</string>
<string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"ప్లే చేయండి"</string>
- <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g>ను తెరవండి"</string>
+ <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"\'<xliff:g id="APP_LABEL">%1$s</xliff:g>\'ను తెరవండి"</string>
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> నుండి <xliff:g id="ARTIST_NAME">%2$s</xliff:g> పాడిన <xliff:g id="SONG_NAME">%1$s</xliff:g>ను ప్లే చేయండి"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> నుండి <xliff:g id="SONG_NAME">%1$s</xliff:g>ను ప్లే చేయండి"</string>
<string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"మీ కోసం"</string>
@@ -1194,7 +1199,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# యాప్ యాక్టివ్గా ఉంది}other{# యాప్లు యాక్టివ్గా ఉన్నాయి}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"కొత్త సమాచారం"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"యాక్టివ్గా ఉన్న యాప్లు"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"మీరు ఈ యాప్లను ఉపయోగించపోయినా కూడా అవి యాక్టివ్గా ఉండి, రన్ అవుతూ ఉంటాయి. దీని వల్ల వాటి ఫంక్షనాలిటీ మెరుగవుతుంది. అయితే ఇది బ్యాటరీ లైఫ్ను కూడా ప్రభావితం చేయవచ్చు."</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"మీరు ఈ యాప్లను ఉపయోగించపోయినా కూడా అవి యాక్టివ్గా ఉండి, రన్ అవుతూ ఉంటాయి. తద్వారా వాటి ఫంక్షనాలిటీ మెరుగవుతుంది. అయితే దీనివల్ల బ్యాటరీ లైఫ్ ప్రభావితం అయ్యే అవకాశం ఉంది."</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ఆపివేయండి"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ఆపివేయబడింది"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"పూర్తయింది"</string>
@@ -1240,7 +1245,7 @@
<string name="log_access_confirmation_body" msgid="6883031912003112634">"మీ పరికరంలో జరిగే దాన్ని పరికర లాగ్లు రికార్డ్ చేస్తాయి. సమస్యలను కనుగొని, పరిష్కరించడానికి యాప్లు ఈ లాగ్లను ఉపయోగిస్తాయి.\n\nకొన్ని లాగ్లలో గోప్యమైన సమాచారం ఉండవచ్చు, కాబట్టి మీరు విశ్వసించే యాప్లను మాత్రమే అన్ని పరికర లాగ్లను యాక్సెస్ చేయడానికి అనుమతించండి. \n\nఅన్ని పరికర లాగ్లను యాక్సెస్ చేయడానికి మీరు ఈ యాప్ను అనుమతించకపోతే, అది తన స్వంత లాగ్లను ఇప్పటికి యాక్సెస్ చేయగలదు. మీ పరికర తయారీదారు ఇప్పటికీ మీ పరికరంలో కొన్ని లాగ్లు లేదా సమాచారాన్ని యాక్సెస్ చేయగలరు."</string>
<string name="log_access_confirmation_learn_more" msgid="3134565480986328004">"మరింత తెలుసుకోండి"</string>
<string name="log_access_confirmation_learn_more_at" msgid="5635666259505215905">"<xliff:g id="URL">%s</xliff:g>లో మరింత తెలుసుకోండి"</string>
- <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g>ను తెరవండి"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"\'<xliff:g id="APPNAME">%1$s</xliff:g>\'ను తెరవండి"</string>
<string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet యాప్ను షార్ట్కట్గా జోడించడానికి, యాప్ ఇన్స్టాల్ చేయబడిందని నిర్ధారించుకోండి"</string>
<string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet యాప్ను షార్ట్కట్గా జోడించడానికి, కనీసం ఒక కార్డ్ జోడించబడిందని నిర్ధారించుకోండి"</string>
<string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home యాప్ను షార్ట్కట్గా జోడించడానికి, యాప్ ఇన్స్టాల్ చేయబడిందని నిర్ధారించుకోండి"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"కీబోర్డ్ బ్యాక్లైట్"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dలో %1$dవ స్థాయి"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"హోమ్ కంట్రోల్స్"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"స్క్రీన్ సేవర్గా మీ హోమ్ కంట్రోల్స్ను త్వరగా యాక్సెస్ చేయండి"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"హోమ్ కంట్రోల్స్ను స్క్రీన్ సేవర్గా చేసి వేగంగా యాక్సెస్ పొందండి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml
index a1ee29f..6584cdd 100644
--- a/packages/SystemUI/res/values-te/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"ఆఫ్లో ఉంది"</item>
<item msgid="5137565285664080143">"ఆన్లో ఉంది"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"అందుబాటులో లేదు"</item>
+ <item msgid="3079622119444911877">"ఆఫ్లో ఉంది"</item>
+ <item msgid="3028994095749238254">"ఆన్లో ఉంది"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index e8d376d..77f506c 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -119,7 +119,7 @@
<string name="screenrecord_taps_label" msgid="1595690528298857649">"แสดงการแตะบนหน้าจอ"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"หยุด"</string>
<string name="screenrecord_share_label" msgid="5025590804030086930">"แชร์"</string>
- <string name="screenrecord_save_title" msgid="1886652605520893850">"บันทึกการบันทึกหน้าจอแล้ว"</string>
+ <string name="screenrecord_save_title" msgid="1886652605520893850">"จัดเก็บการบันทึกหน้าจอไว้แล้ว"</string>
<string name="screenrecord_save_text" msgid="3008973099800840163">"แตะเพื่อดู"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"เกิดข้อผิดพลาดในการบันทึกหน้าจอ"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"เกิดข้อผิดพลาดขณะเริ่มบันทึกหน้าจอ"</string>
@@ -260,7 +260,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวนอน"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวตั้ง"</string>
<string name="dessert_case" msgid="9104973640704357717">"ชั้นแสดงของหวาน"</string>
- <string name="start_dreams" msgid="9131802557946276718">"โปรแกรมรักษาหน้าจอ"</string>
+ <string name="start_dreams" msgid="9131802557946276718">"ภาพพักหน้าจอ"</string>
<string name="ethernet_label" msgid="2203544727007463351">"อีเทอร์เน็ต"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"ห้ามรบกวน"</string>
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"บลูทูธ"</string>
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ดูทั้งหมด"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"ใช้บลูทูธ"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"เชื่อมต่อแล้ว"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"บันทึกแล้ว"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ยกเลิกการเชื่อมต่อ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"เปิดใช้งาน"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"เปิดอีกครั้งโดยอัตโนมัติในวันพรุ่งนี้"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ฟีเจอร์ต่างๆ เช่น Quick Share, หาอุปกรณ์ของฉัน และตำแหน่งของอุปกรณ์ ใช้บลูทูธ"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"บลูทูธจะเปิดพรุ่งนี้เวลา 05:00 น."</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"เสียง"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ชุดหูฟัง"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"ปัญหาการบันทึก"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"เริ่ม"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"หยุด"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"รายงานข้อบกพร่อง"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ประสบการณ์การใช้งานอุปกรณ์ส่วนใดที่ได้รับผลกระทบ"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"เลือกประเภทปัญหา"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"บันทึกหน้าจอ"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"มาตรฐาน"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"ปานกลาง"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"สูง"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"เครื่องช่วยฟัง"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"เครื่องช่วยฟัง"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"จับคู่อุปกรณ์ใหม่"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"คลิกเพื่อจับคู่อุปกรณ์ใหม่"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"เลิกบล็อกไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"เลิกบล็อกกล้องของอุปกรณ์ใช่ไหม"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"เลิกบล็อกกล้องและไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
@@ -745,12 +750,12 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"สลับรูปแบบแป้นพิมพ์"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"หรือ"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ล้างคำค้นหา"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"แป้นพิมพ์ลัด"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"แป้นพิมพ์ลัด"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ค้นหาแป้นพิมพ์ลัด"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ไม่พบแป้นพิมพ์ลัด"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"ระบบ"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"อินพุต"</string>
- <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"แอปที่เปิดอยู่"</string>
+ <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"เปิดแอป"</string>
<string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"แอปปัจจุบัน"</string>
<string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"แสดงผลการค้นหา"</string>
<string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"แสดงแป้นพิมพ์ลัดสำหรับระบบ"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"เปิด Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ล็อกหน้าจอ"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"จดโน้ต"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"การทํางานหลายอย่างพร้อมกันของระบบ"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"เข้าสู่โหมดแยกหน้าจอโดยแอปปัจจุบันอยู่ด้านขวา"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"เข้าสู่โหมดแยกหน้าจอโดยแอปปัจจุบันอยู่ด้านซ้าย"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"การทํางานหลายอย่างพร้อมกัน"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"ใช้โหมดแยกหน้าจอโดยแอปปัจจุบันอยู่ด้านขวา"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"ใช้โหมดแยกหน้าจอโดยแอปปัจจุบันอยู่ด้านซ้าย"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"เปลี่ยนจากโหมดแยกหน้าจอเป็นเต็มหน้าจอ"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"เปลี่ยนไปใช้แอปทางด้านขวาหรือด้านล่างขณะใช้โหมดแยกหน้าจอ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"เปลี่ยนไปใช้แอปทางด้านซ้ายหรือด้านบนขณะใช้โหมดแยกหน้าจอ"</string>
@@ -1194,7 +1199,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{ทำงานอยู่ # แอป}other{ทำงานอยู่ # แอป}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"ข้อมูลใหม่"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"แอปที่ทำงานอยู่"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"แอปเหล่านี้กำลังทำงานแม้ว่าคุณจะไม่ได้ใช้งานอยู่ก็ตาม วิธีนี้ช่วยให้ฟังก์ชันการทำงานของแอปมีประสิทธิภาพมากขึ้น แต่ก็อาจส่งผลต่ออายุการใช้งานแบตเตอรี่ด้วย"</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"แอปเหล่านี้กำลังทำงานแม้ว่าคุณจะไม่ได้ใช้งานอยู่ก็ตาม วิธีนี้ช่วยให้ฟังก์ชันการทำงานของแอปมีประสิทธิภาพมากขึ้น แต่ก็อาจส่งผลต่อระยะเวลาการใช้งานแบตเตอรี่ด้วย"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"หยุด"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"หยุดแล้ว"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"เสร็จ"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"เข้าถึงระบบควบคุมอุปกรณ์สมาร์ทโฮมได้อย่างรวดเร็วที่การพักหน้าจอ"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"เข้าถึงระบบควบคุมอุปกรณ์สมาร์ทโฮมได้อย่างรวดเร็วผ่านภาพพักหน้าจอ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml
index d6351ce..8b7187b 100644
--- a/packages/SystemUI/res/values-th/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"ปิด"</item>
<item msgid="5137565285664080143">"เปิด"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"ไม่พร้อมใช้งาน"</item>
+ <item msgid="3079622119444911877">"ปิด"</item>
+ <item msgid="3028994095749238254">"เปิด"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 54bb74b..922bf88 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Tingnan lahat"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Gumamit ng Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Nakakonekta"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Na-save"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"idiskonekta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"i-activate"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Awtomatikong i-on ulit bukas"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Guamgamit ng Bluetooth ang mga feature tulad ng Quick Share, Hanapin ang Aking Device, at lokasyon ng device"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Mag-o-on ang Bluetooth bukas nang 5 AM"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> na baterya"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Mag-record ng Isyu"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Magsimula"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Ihinto"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Ulat ng Bug"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ano\'ng naapektuhang parte ng experience sa device?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Piliin ang uri ng isyu"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pag-record ng screen"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Katamtaman"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Mataas"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Mga hearing device"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Mga hearing device"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Magpares ng bagong device"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"I-click para magpares ng bagong device"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"I-unblock ang mikropono ng device?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"I-unblock ang camera ng device?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"I-unblock ang camera at mikropono ng device?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Magpalit ng layout ng keyboard"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"o"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"I-clear ang query sa paghahanap"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Mga Shortcut"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Mga Keyboard Shortcut"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Maghanap ng mga shortcut"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Walang nakitang shortcut"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"System"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Buksan ang assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"I-lock ang screen"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Magtala"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking ng system"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Lumipat sa split screen nang nasa RHS ang kasalukuyang app"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Lumipat sa split screen nang nasa LHS ang kasalukuyang app"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Pag-multitask"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Gumamit ng split screen nang nasa kanan ang kasalukuyang app"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Gumamit ng split screen nang nasa kaliwa ang kasalukuyang app"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Lumipat sa full screen mula sa split screen"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Lumipat sa app sa kanan o ibaba habang ginagamit ang split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Lumipat sa app sa kaliwa o itaas habang ginagamit ang split screen"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 5364c83..35f13de 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Tümünü göster"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth\'u kullan"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Bağlandı"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Kaydedildi"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"bağlantıyı kes"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"etkinleştir"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Yarın otomatik olarak tekrar aç"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, Cihazımı Bul ve cihaz konumu gibi özellikler Bluetooth\'u kullanır"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth yarın saat 05:00\'te açılacak"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Sorunu Kaydedin"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Başlayın"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Durdurun"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Hata Raporu"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz deneyiminiz ne şekilde etkilendi?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sorun türünü seçin"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran kaydedicisi"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Orta"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Yüksek"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"İşitme cihazları"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"İşitme cihazları"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz eşle"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz eşlemek için tıklayın"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonunun engellemesi kaldırılsın mı?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerasının engellemesi kaldırılsın mı?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası ile mikrofonunun engellemesi kaldırılsın mı?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Klavye düzenini değiştir"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"veya"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Arama sorgusunu temizle"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Kısayollar"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Klavye Kısayolları"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Kısayol araması yapın"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Kısayol bulunamadı"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistem"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Asistan\'ı aç"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kilit ekranı"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Not al"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Sistem çoklu görevi"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Mevcut uygulamayı sağ tarafa alarak bölünmüş ekrana geç"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Mevcut uygulamayı sol tarafa alarak bölünmüş ekrana geç"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Çoklu görev"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Sağdaki mevcut uygulamayla birlikte bölünmüş ekranı kullanın"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Soldaki mevcut uygulamayla birlikte bölünmüş ekranı kullanın"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Bölünmüş ekrandan tam ekrana geç"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bölünmüş ekran kullanırken sağdaki veya alttaki uygulamaya geçiş yapın"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bölünmüş ekran kullanırken soldaki veya üstteki uygulamaya geçiş yapın"</string>
@@ -1304,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ev Kontrolleri"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Ekran koruyucu olan ev kontrollerinize hızlıca erişin"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Ekran koruyucu olarak ev kontrollerinize hızla erişin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index da1dfa4..0d97121 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Показати всі"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Увімкнути Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Підключено"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Збережено"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"від’єднати"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активувати"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично ввімкнути знову завтра"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Такі функції, як швидкий обмін, \"Знайти пристрій\" і визначення місцезнаходження пристрою, використовують Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth увімкнеться завтра о 5:00"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Запис помилки"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Почати"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Зупинити"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Звіт про помилку"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"На який аспект роботи пристрою вплинула проблема?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Виберіть тип проблеми"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис відео з екрана"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандартний"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Середній"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Високий"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухові апарати"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухові апарати"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Підключити новий пристрій"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Натисніть, щоб підключити новий пристрій"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Надати доступ до мікрофона?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Надати доступ до камери пристрою?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Надати доступ до камери й мікрофона?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Змінити розкладку клавіатури"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"або"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Очистити пошуковий запит"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Швидкі команди"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Комбінації клавіш"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Пошук швидких команд"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Швидк. команд не знайдено"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Система"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Відкрити додаток Асистент"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Заблокувати екран"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Створити нотатку"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Багатозадачність системи"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Розділити екран із поточним додатком праворуч"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Розділити екран із поточним додатком ліворуч"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Багатозадачність"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Розділити екран і показувати поточний додаток праворуч"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Розділити екран і показувати поточний додаток ліворуч"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Перейти з розділення екрана на весь екран"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Під час розділення екрана перемикатися на додаток праворуч або внизу"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Під час розділення екрана перемикатися на додаток ліворуч або вгорі"</string>
@@ -784,7 +789,7 @@
<string name="input_access_voice_typing" msgid="7291201476395326141">"Відкрити голосовий ввід"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Додатки"</string>
<string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"Асистент"</string>
- <string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Веб-переглядач"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Вебпереглядач"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"Контакти"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"Електронна пошта"</string>
<string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS"</string>
@@ -903,7 +908,7 @@
<string name="instant_apps_message" msgid="6112428971833011754">"Додаток відкрито без встановлення."</string>
<string name="instant_apps_message_with_help" msgid="1816952263531203932">"Додаток відкрито без встановлення. Торкніться, щоб дізнатися більше."</string>
<string name="app_info" msgid="5153758994129963243">"Про додаток"</string>
- <string name="go_to_web" msgid="636673528981366511">"Веб-переглядач"</string>
+ <string name="go_to_web" msgid="636673528981366511">"Вебпереглядач"</string>
<string name="mobile_data" msgid="4564407557775397216">"Мобільний трафік"</string>
<string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
index be779eb..61e62e4 100644
--- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"Вимкнено"</item>
<item msgid="5137565285664080143">"Увімкнено"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"Недоступно"</item>
+ <item msgid="3079622119444911877">"Вимкнено"</item>
+ <item msgid="3028994095749238254">"Увімкнено"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index fa4bd02..888be9c 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"سبھی دیکھیں"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"بلوٹوتھ استعمال کریں"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"منسلک ہے"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ ہے"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"غیر منسلک کریں"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کریں"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن ہوگا"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"فوری اشتراک، میرا آلہ ڈھونڈیں، اور آلہ کے مقام جیسی خصوصیات بلوٹوتھ کا استعمال کرتی ہیں"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"بلوٹوتھ کل صبح 5 بجے آن ہو جائے گا"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> بیٹری"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"مسئلہ ریکارڈ کریں"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"شروع کریں"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"روکیں"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"بگ رپورٹ"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"آپ کے آلے کے تجربے کا کون سا حصہ متاثر ہوا؟"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"مسئلہ کی قسم منتخب کریں"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"اسکرین ریکارڈ"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"متوسط"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"زیادہ"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعت کے آلات"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سماعت کے آلات"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"نئے آلے کا جوڑا بنائیں"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"نئے آلے کا جوڑا بنانے کے لیے کلک کریں"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"آلے کا مائیکروفون غیر مسدود کریں؟"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"آلے کا کیمرا غیر مسدود کریں؟"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"آلے کا کیمرا اور مائیکروفون غیر مسدود کریں؟"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"کی بورڈ لے آؤٹ سوئچ کریں"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"یا"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"تلاش کا استفسار صاف کریں"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"شارٹ کٹس"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"کی بورڈ شارٹ کٹس"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"شارٹ کٹس تلاش کریں"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"کوئی شارٹ کٹ نہیں ملا"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"سسٹم"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"اسسٹنٹ کھولیں"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"مقفل اسکرین"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"نوٹ لیں"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"سسٹم ملٹی ٹاسکنگ"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"موجودہ ایپ کے ساتھ دائیں جانب اسپلٹ اسکرین انٹر کریں"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"موجودہ ایپ کے ساتھ بائیں جانب اسپلٹ اسکرین انٹر کریں"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ملٹی ٹاسکنگ"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"دائیں جانب موجودہ ایپ کے ساتھ اسپلٹ اسکرین کا استعمال کریں"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"بائیں جانب موجودہ ایپ کے ساتھ اسپلٹ اسکرین کا استعمال کریں"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"اسپلٹ اسکرین سے پوری سکرین پر سوئچ کریں"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"اسپلٹ اسکرین کا استعمال کرتے ہوئے دائیں یا نیچے ایپ پر سوئچ کریں"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"اسپلٹ اسکرین کا استعمال کرتے ہوئے بائیں یا اوپر ایپ پر سوئچ کریں"</string>
@@ -1123,7 +1129,7 @@
<string name="build_number_copy_toast" msgid="877720921605503046">"بلڈ نمبر کلپ بورڈ میں کاپی ہو گیا۔"</string>
<string name="basic_status" msgid="2315371112182658176">"گفتگو کھولیں"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"گفتگو ویجیٹس"</string>
- <string name="select_conversation_text" msgid="3376048251434956013">"اسے اپنے ہوم اسکرین پر شامل کرنے کے لیے گفتگو پر تھپتھپائیں"</string>
+ <string name="select_conversation_text" msgid="3376048251434956013">"اسے اپنی ہوم اسکرین پر شامل کرنے کے لیے گفتگو پر تھپتھپائیں"</string>
<string name="no_conversations_text" msgid="5354115541282395015">"آپ کی حالیہ گفتگوئیں یہاں دکھائی دیں گی"</string>
<string name="priority_conversations" msgid="3967482288896653039">"ترجیحی گفتگوئیں"</string>
<string name="recent_conversations" msgid="8531874684782574622">"حالیہ گفتگوئیں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 195b044..6581db5 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Hammasi"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth ishlatish"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ulangan"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saqlangan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"uzish"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"faollashtirish"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ertaga yana avtomatik yoqilsin"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Tezkor ulashuv, Qurilmamni top va qurilma geolokatsiyasi kabi funksiyalar Bluetooth ishlatadi"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ertaga soat 5 da yoqiladi"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Yozib olishda xato"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Boshlash"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Toʻxtatish"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Xatoliklar hisoboti"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Qurilma ishlashining qaysi qismiga taʼsir qildi?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Muammo turini tanlang"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran yozuvi"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Oʻrtacha"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Yuqori"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eshitish qurilmalari"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Eshitish qurilmalari"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yangi qurilmani ulash"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yangi qurilmani ulash uchun bosing"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Qurilma mikrofoni blokdan chiqarilsinmi?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Qurilma kamerasi blokdan chiqarilsinmi?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Qurilma kamerasi va mikrofoni blokdan chiqarilsinmi?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Klaviatura terilmasini almashtirish"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"yoki"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Qidiruv soʻrovini tozalash"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Tezkor tugmalar"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Tezkor tugmalar"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Tezkor tugmalar qidiruvi"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Tezkor tugmalar topilmadi"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Tizim"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistentni ochish"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Ekran qulfi"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Qayd yaratish"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Tizimdagi multi-vazifalik"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Oʻng tomondagi ajratilgan ekran rejimiga kirish"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Chap tomondagi ajratilgan ekran rejimiga kirish"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multi-vazifalilik"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Ekranni ajratib, joriy ilovani oʻngga joylash"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Ekranni ajratib, joriy ilovani chapga joylash"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Ajratilgan ekran rejimidan butun ekranga almashtirish"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Ajratilgan ekranda oʻngdagi yoki pastdagi ilovaga almashish"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ajratilgan ekranda chapdagi yoki yuqoridagi ilovaga almashish"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 469430b..cce810e 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Xem tất cả"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Bật Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Đã kết nối"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Đã lưu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ngắt kết nối"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"kích hoạt"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Tự động bật lại vào ngày mai"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Các tính năng như Chia sẻ nhanh, Tìm thiết bị của tôi và dịch vụ vị trí trên thiết bị có sử dụng Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth sẽ bật vào ngày mai lúc 5 giờ sáng"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> pin"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Ghi lại vấn đề"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Bắt đầu"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Dừng"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Báo cáo lỗi"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bạn gặp loại vấn đề gì khi dùng thiết bị?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chọn loại vấn đề"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ghi màn hình"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Vừa"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Cao"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Thiết bị trợ thính"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Thiết bị trợ thính"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Ghép nối thiết bị mới"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Nhấp để ghép nối thiết bị mới"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Bỏ chặn micrô của thiết bị?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Bỏ chặn camera của thiết bị?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Bỏ chặn máy ảnh và micrô của thiết bị?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Chuyển đổi bố cục bàn phím"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"hoặc"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Xoá cụm từ tìm kiếm"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Lối tắt"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Phím tắt"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Lối tắt tìm kiếm"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Không tìm thấy lối tắt"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Hệ thống"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Mở Trợ lý"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Màn hình khoá"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Tạo ghi chú"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Xử lý đa nhiệm trong hệ thống"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Vào chế độ chia đôi màn hình, ứng dụng hiện tại ở màn hình bên phải"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Vào chế độ chia đôi màn hình, ứng dụng hiện tại ở màn hình bên trái"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Đa nhiệm"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Dùng tính năng chia đôi màn hình, trong đó ứng dụng hiện tại ở bên phải"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Dùng tính năng chia đôi màn hình, trong đó ứng dụng hiện tại ở bên trái"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Chuyển từ chế độ chia đôi màn hình sang chế độ toàn màn hình"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Chuyển sang ứng dụng bên phải hoặc ở dưới khi đang chia đôi màn hình"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Chuyển sang ứng dụng bên trái hoặc ở trên khi đang chia đôi màn hình"</string>
@@ -994,7 +1000,7 @@
<string name="accessibility_floating_button_undo" msgid="511112888715708241">"Huỷ"</string>
<string name="accessibility_floating_button_hidden_notification_title" msgid="4115036997406994799">"Đã ẩn nút hỗ trợ tiếp cận"</string>
<string name="accessibility_floating_button_hidden_notification_text" msgid="1457021647040915658">"Nhấn để hiện nút hỗ trợ tiếp cận"</string>
- <string name="accessibility_floating_button_undo_message_label_text" msgid="9017658016426242640">"Đã xoá phím tắt dành cho <xliff:g id="FEATURE_NAME">%s</xliff:g>"</string>
+ <string name="accessibility_floating_button_undo_message_label_text" msgid="9017658016426242640">"Đã xoá lối tắt cho <xliff:g id="FEATURE_NAME">%s</xliff:g>"</string>
<string name="accessibility_floating_button_undo_message_number_text" msgid="4909270290725226075">"{count,plural, =1{Đã xoá # lối tắt}other{Đã xoá # lối tắt}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Chuyển lên trên cùng bên trái"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Chuyển lên trên cùng bên phải"</string>
@@ -1283,7 +1289,7 @@
<string name="mirror_display" msgid="2515262008898122928">"Phản chiếu màn hình"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Đóng"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Đã kết nối màn hình"</string>
- <string name="privacy_dialog_title" msgid="7839968133469098311">"Micrô và máy ảnh"</string>
+ <string name="privacy_dialog_title" msgid="7839968133469098311">"Micrô và camera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Hoạt động sử dụng gần đây của ứng dụng"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Xem hoạt động truy cập gần đây"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"Xong"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 6765d1e..f683133 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"查看全部"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"使用蓝牙"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已连接"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已保存"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"断开连接"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"启用"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自动重新开启"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"“快速分享”“查找我的设备”“设备位置信息”等功能会使用蓝牙"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"蓝牙将于明天早晨 5 点开启"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> 的电量"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"录制问题"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"开始"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"错误报告"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"设备体验的哪个方面受到影响?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"选择问题类型"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"屏幕录制"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"标准"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"中"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"高"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助听装置"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助听装置"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"与新设备配对"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"点击即可与新设备配对"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解锁设备麦克风吗?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解锁设备摄像头吗?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解锁设备摄像头和麦克风吗?"</string>
@@ -686,7 +691,7 @@
<string name="notification_unblockable_call_desc" msgid="5907328164696532169">"无法修改来电通知。"</string>
<string name="notification_multichannel_desc" msgid="7414593090056236179">"您无法在此处配置这组通知"</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"代理通知"</string>
- <string name="notification_channel_dialog_title" msgid="6856514143093200019">"所有的<xliff:g id="APP_NAME">%1$s</xliff:g>通知"</string>
+ <string name="notification_channel_dialog_title" msgid="6856514143093200019">"所有“<xliff:g id="APP_NAME">%1$s</xliff:g>”通知"</string>
<string name="see_more_title" msgid="7409317011708185729">"查看更多"</string>
<string name="feedback_alerted" msgid="5192459808484271208">"系统已自动将此通知的重要性<b>提升为“默认”</b>。"</string>
<string name="feedback_silenced" msgid="9116540317466126457">"系统已自动将此通知的重要性<b>降低为“静音”</b>。"</string>
@@ -745,12 +750,12 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"切换键盘布局"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"或"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"清除搜索查询"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"快捷键"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"键盘快捷键"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"搜索快捷键"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"未找到任何快捷键"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"系统"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"输入"</string>
- <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"已开应用"</string>
+ <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"打开应用"</string>
<string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"当前应用"</string>
<string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"目前显示的是搜索结果"</string>
<string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"目前显示的是系统快捷键"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"打开 Google 助理"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"锁定屏幕"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"添加记事"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"系统多任务处理"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"进入分屏模式,当前应用显示于右侧"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"进入分屏模式,当前应用显示于左侧"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"多任务处理"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"使用分屏模式,并且当前应用位于右侧"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"使用分屏模式,并且当前应用位于左侧"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"从分屏模式切换为全屏"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分屏模式时,切换到右侧或下方的应用"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分屏模式时,切换到左侧或上方的应用"</string>
@@ -830,7 +835,7 @@
<string name="left_icon" msgid="5036278531966897006">"向左图标"</string>
<string name="right_icon" msgid="1103955040645237425">"向右图标"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"按住并拖动即可添加功能块"</string>
- <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"按住并拖动即可重新排列图块"</string>
+ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"按住并拖动即可重新排列功能块"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"拖动到此处即可移除"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"您至少需要 <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 个卡片"</string>
<string name="qs_edit" msgid="5583565172803472437">"编辑"</string>
@@ -1194,7 +1199,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 个应用处于活动状态}other{# 个应用处于活动状态}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"新信息"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"已开启的应用"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"这些应用正在保持活跃运行状态,即使您没有在使用它们。这可以改进它们的功能,但可能会影响到电池续航时间。"</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"这些应用即使在闲置时仍处于活跃运行状态。应用的功能会因此提升,但可能会影响电池续航时间。"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"已停止"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"完成"</string>
@@ -1285,7 +1290,7 @@
<string name="dismiss_dialog" msgid="2195508495854675882">"关闭"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"显示屏已连接"</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-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index a9a377a..9467b9b 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"已关闭"</item>
<item msgid="5137565285664080143">"已开启"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"不可用"</item>
+ <item msgid="3079622119444911877">"关闭"</item>
+ <item msgid="3028994095749238254">"开启"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index d4891d5..e6fcb7f 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"查看全部"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"使用藍牙"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已連接"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"解除連結"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟動"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"「快速共享」、「尋找我的裝置」和裝置位置等功能都會使用藍牙"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"藍牙將於明天上午 5 時開啟"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"錄製問題"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"開始"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"錯誤報告"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受影響?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"標準"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"中"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"高"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助聽器"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"配對新裝置"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"㩒一下就可以配對新裝置"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解除封鎖裝置相機和麥克風嗎?"</string>
@@ -745,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"切換鍵盤配置"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"或"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"清除搜尋查詢"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"快速鍵"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"鍵盤快速鍵"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"搜尋快速鍵"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"找不到快速鍵"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"系統"</string>
@@ -770,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"開啟「Google 助理」"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"上鎖畫面"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"寫筆記"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"系統多工處理"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"進入分割螢幕模式,並將目前的應用程式顯示在右側"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"進入分割螢幕模式,並將目前的應用程式顯示在左側"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"多工處理"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"使用分割螢幕,並在右側顯示目前使用的應用程式"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"使用分割螢幕,並在左側顯示目前使用的應用程式"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"將分割螢幕切換為全螢幕"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分割螢幕時,切換至右邊或下方的應用程式"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分割螢幕時,切換至左邊或上方的應用程式"</string>
@@ -1305,5 +1310,5 @@
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"智能家居"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"在螢幕保護程式畫面上快速存取家居控制功能"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"在螢幕保護程式畫面上控制智能家居"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
index f0ccd9e..cca7ac4 100644
--- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"關閉"</item>
<item msgid="5137565285664080143">"開啟"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"無法使用"</item>
+ <item msgid="3079622119444911877">"關閉"</item>
+ <item msgid="3028994095749238254">"開啟"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index bc59988..b02bf81 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"查看全部"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"使用藍牙"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已連線"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"取消連結"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟用"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"藍牙會用於快速分享、「尋找我的裝置」,以及裝置位置資訊等功能"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"藍牙將在明天早上 5 點開啟"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"記錄問題"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"開始"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"錯誤報告"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受到影響?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string>
@@ -363,14 +372,10 @@
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"標準"</string>
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"中"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"高"</string>
- <!-- no translation found for quick_settings_hearing_devices_label (7277170419679404129) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string>
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助聽器"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"配對新裝置"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"按一下即可配對新裝置"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要將裝置的相機和麥克風解除封鎖嗎?"</string>
@@ -678,7 +683,7 @@
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>狀態:</b>已調降順序"</string>
<string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"顯示在對話通知頂端 (螢幕鎖定時會顯示為個人資料相片)"</string>
<string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"以對話框的形式顯示在對話通知頂端 (螢幕鎖定時會顯示為個人資料相片)"</string>
- <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"顯示在對話通知頂端 (螢幕鎖定時會顯示為個人資料相片),並會中斷「零打擾」模式"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"顯示在對話通知頂端 (螢幕鎖定時會顯示個人資料相片),並會中斷「零打擾」模式"</string>
<string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"以對話框的形式顯示在對話通知頂端 (螢幕鎖定時會顯示為個人資料相片),並會中斷「零打擾」模式"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"優先"</string>
<string name="no_shortcut" msgid="8257177117568230126">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」不支援對話功能"</string>
@@ -745,12 +750,12 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"切換鍵盤配置"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"或"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"清除搜尋查詢"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"快速鍵"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"鍵盤快速鍵"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"搜尋快速鍵"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"找不到快速鍵"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"系統"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"輸入"</string>
- <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"已開啟的應用程式"</string>
+ <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"開啟應用程式"</string>
<string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"目前的應用程式"</string>
<string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"顯示搜尋結果"</string>
<string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"顯示系統快速鍵"</string>
@@ -763,16 +768,16 @@
<string name="group_system_go_back" msgid="2730322046244918816">"返回"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"前往主畫面"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"查看最近開啟的應用程式"</string>
- <string name="group_system_cycle_forward" msgid="5478663965957647805">"前往最近開啟的應用程式"</string>
- <string name="group_system_cycle_back" msgid="8194102916946802902">"返回最近開啟的應用程式"</string>
+ <string name="group_system_cycle_forward" msgid="5478663965957647805">"向前循環切換最近開啟的應用程式"</string>
+ <string name="group_system_cycle_back" msgid="8194102916946802902">"向後循環切換最近開啟的應用程式"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"開啟應用程式清單"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"開啟設定"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"開啟 Google 助理"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"螢幕鎖定"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"新增記事"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"系統多工處理"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"進入分割畫面模式,並將目前的應用程式顯示於右側"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"進入分割畫面模式,並將目前的應用程式顯示於左側"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"多工處理"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"使用分割畫面,並在右側顯示目前使用的應用程式"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"使用分割畫面,並在左側顯示目前使用的應用程式"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"從分割畫面切換到完整畫面"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分割畫面時,切換到右邊或上方的應用程式"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分割畫面時,切換到左邊或上方的應用程式"</string>
@@ -868,7 +873,7 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"編輯設定順序。"</string>
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"電源鍵選單"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"第 <xliff:g id="ID_1">%1$d</xliff:g> 頁,共 <xliff:g id="ID_2">%2$d</xliff:g> 頁"</string>
- <string name="tuner_lock_screen" msgid="2267383813241144544">"鎖定畫面"</string>
+ <string name="tuner_lock_screen" msgid="2267383813241144544">"螢幕鎖定"</string>
<string name="finder_active" msgid="7907846989716941952">"即使這支手機關機,仍可透過「尋找我的裝置」找出手機位置"</string>
<string name="shutdown_progress" msgid="5464239146561542178">"關機中…"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"查看處理步驟"</string>
@@ -1194,7 +1199,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 個應用程式正在運作}other{# 個應用程式正在運作}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"新資訊"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"運作中的應用程式"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"即使你並未使用,這些應用程式仍會持續運作。這可提升應用程式效能,但也可能影響電池續航力。"</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"這些應用程式會在沒有使用的情況下持續運作。應用程式的實用度會因此提升,但也可能影響電池續航力。"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"已停止"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"完成"</string>
@@ -1285,7 +1290,7 @@
<string name="dismiss_dialog" msgid="2195508495854675882">"關閉"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"螢幕已連結"</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-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
index 2c474f6..4cc5804 100644
--- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
@@ -186,7 +186,9 @@
<item msgid="2478289035899842865">"關閉"</item>
<item msgid="5137565285664080143">"開啟"</item>
</string-array>
- <!-- no translation found for tile_states_hearing_devices:0 (1235334096484287173) -->
- <!-- no translation found for tile_states_hearing_devices:1 (3079622119444911877) -->
- <!-- no translation found for tile_states_hearing_devices:2 (3028994095749238254) -->
+ <string-array name="tile_states_hearing_devices">
+ <item msgid="1235334096484287173">"無法使用"</item>
+ <item msgid="3079622119444911877">"已關閉"</item>
+ <item msgid="3028994095749238254">"已開啟"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 780fe94..a4d66a4 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -260,7 +260,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Isikrini sikhiyelwe ngomumo we-landscape."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Isikrini sikhiyelwe ngomumo we-portrait."</string>
<string name="dessert_case" msgid="9104973640704357717">"Isikhwama soswidi"</string>
- <string name="start_dreams" msgid="9131802557946276718">"Isigcini sihenqo"</string>
+ <string name="start_dreams" msgid="9131802557946276718">"Isigciniskrini"</string>
<string name="ethernet_label" msgid="2203544727007463351">"I-Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ungaphazamisi"</string>
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"I-Bluetooth"</string>
@@ -270,12 +270,20 @@
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Buka konke"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Sebenzisa i-Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ixhunyiwe"</string>
+ <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+ <skip />
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ilondoloziwe"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"nqamula"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"yenza kusebenze"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Vula ngokuzenzekela futhi kusasa"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Izakhi ezifana Nokwabelana Ngokushesha, okuthi Thola Idivayisi Yami, kanye nendawo yedivayisi zisebenzisa i-Bluetooth"</string>
- <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"I-Bluetooth izovulwa kusasa ngo-5 AM"</string>
+ <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+ <skip />
+ <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string>
@@ -350,6 +358,7 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Rekhoda Inkinga"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Qala"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Misa"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Umbiko Wesiphazamisi"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuthinteke yiphi ingxenye yokusebenzisa idivayisi?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Khetha uhlobo lwenkinga"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Irekhodi lesikrini"</string>
@@ -364,12 +373,9 @@
<string name="quick_settings_contrast_medium" msgid="5158352575583902566">"Okuphakathi"</string>
<string name="quick_settings_contrast_high" msgid="656049259587494499">"Phezulu"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Izinsizakuzwa"</string>
- <!-- no translation found for quick_settings_hearing_devices_dialog_title (9004774017688484981) -->
- <skip />
- <!-- no translation found for quick_settings_pair_hearing_devices (5987105102207447322) -->
- <skip />
- <!-- no translation found for accessibility_hearing_device_pair_new_device (8440082580186130090) -->
- <skip />
+ <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Izinsizakuzwa"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bhangqa idivayisi entsha"</string>
+ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Chofoza ukuze ubhangqe idivayisi entsha"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vulela imakrofoni yedivayisi?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vulela ikhamera yedivayisi?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vulela ikhamera yedivayisi nemakrofoni?"</string>
@@ -744,7 +750,7 @@
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Shintsha isakhiwo sekhibhodi"</string>
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"noma"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Sula umbuzo wosesho"</string>
- <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Izinqamuleli"</string>
+ <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Izinqamuleli Zekhibhodi"</string>
<string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Sesha izinqamuleli"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Azikho izinqamuleli ezitholakele"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Isistimu"</string>
@@ -769,9 +775,9 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Vula umsizi"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Khiya isikrini"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Thatha inothi"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Ukwenza imisebenzi eminingi yesistimu"</string>
- <string name="system_multitasking_rhs" msgid="2454557648974553729">"Faka ukuhlukanisa isikrini nge-app yamanje kuya ku-RHS"</string>
- <string name="system_multitasking_lhs" msgid="3516599774920979402">"Faka ukuhlukanisa isikrini nge-app yamanje kuya ku-LHS"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Ukwenza imisebenzi eminingi"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Sebenzisa isikrini esihlukanisayo nge-app yamanje kwesokudla"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Sebenzisa isikrini sokuhlukanisa nge-app yamanje kwesokunxele"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Shintsha usuka ekuhlukaniseni isikrini uye kusikrini esigcwele"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Shintshela ku-app ngakwesokudla noma ngezansi ngenkathi usebenzisa uhlukanisa isikrini"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Shintshela ku-app ngakwesokunxele noma ngaphezulu ngenkathi usebenzisa ukuhlukanisa isikrini"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index a6f6d4d..fa9d507d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -116,9 +116,6 @@
The syntax is setting-name:spec. If the tile is a TileService, the spec should be specified
as custom(package/class). Relative class name is supported. -->
<string-array name="config_quickSettingsAutoAdd" translatable="false">
- <item>accessibility_display_daltonizer_enabled:color_correction</item>
- <item>accessibility_display_inversion_enabled:inversion</item>
- <item>one_handed_mode_enabled:onehanded</item>
<item>accessibility_font_scaling_has_been_changed:font_scaling</item>
</string-array>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b0b5482..26fa2b1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -613,7 +613,7 @@
<dimen name="volume_panel_slice_vertical_padding">8dp</dimen>
<dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
- <dimen name="volume_panel_corner_radius">52dp</dimen>
+ <dimen name="volume_panel_corner_radius">28dp</dimen>
<!-- Size of each item in the ringer selector drawer. -->
<dimen name="volume_ringer_drawer_item_size">42dp</dimen>
@@ -2001,4 +2001,7 @@
<!-- SliceView grid gutter for ANC Slice -->
<dimen name="abc_slice_grid_gutter">0dp</dimen>
+ <!-- SliceView icon size -->
+ <dimen name="abc_slice_big_pic_min_height">64dp</dimen>
+ <dimen name="abc_slice_big_pic_max_height">64dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 035cfdc..b993a5a 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -186,6 +186,8 @@
<item type="id" name="action_remove_menu"/>
<item type="id" name="action_edit"/>
+ <item type="id" name="accessibility_action_open_communal_hub"/>
+
<!-- rounded corner view id -->
<item type="id" name="rounded_corner_top_left"/>
<item type="id" name="rounded_corner_top_right"/>
@@ -231,6 +233,7 @@
<item type="id" name="smart_space_barrier_bottom" />
<item type="id" name="small_clock_guideline_top" />
<item type="id" name="weather_clock_date_and_icons_barrier_bottom" />
+ <item type="id" name="accessibility_actions_view" />
<!-- Privacy dialog -->
<item type="id" name="privacy_dialog_close_app_button" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index af661aa..4690f02 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -568,7 +568,7 @@
<!-- Content description for the split notification shade that also includes QS (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_qs_notification_shade">Quick settings and Notification shade.</string>
<!-- Content description for the lock screen (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_desc_lock_screen">Lock screen.</string>
+ <string name="accessibility_desc_lock_screen">Lock screen</string>
<!-- Content description for the work profile lock screen. This prevents work profile apps from being used, but personal apps can be used as normal (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_work_lock">Work lock screen</string>
<!-- Content description for the close button in the zen mode panel introduction message. [CHAR LIMIT=NONE] -->
@@ -678,6 +678,8 @@
<string name="turn_on_bluetooth">Use Bluetooth</string>
<!-- QuickSettings: Bluetooth dialog device connected default summary [CHAR LIMIT=NONE]-->
<string name="quick_settings_bluetooth_device_connected">Connected</string>
+ <!-- QuickSettings: Bluetooth dialog device in audio sharing default summary [CHAR LIMIT=50]-->
+ <string name="quick_settings_bluetooth_device_audio_sharing">Audio Sharing</string>
<!-- QuickSettings: Bluetooth dialog device saved default summary [CHAR LIMIT=NONE]-->
<string name="quick_settings_bluetooth_device_saved">Saved</string>
<!-- QuickSettings: Accessibility label to disconnect a device [CHAR LIMIT=NONE]-->
@@ -687,9 +689,13 @@
<!-- QuickSettings: Bluetooth auto on tomorrow [CHAR LIMIT=NONE]-->
<string name="turn_on_bluetooth_auto_tomorrow">Automatically turn on again tomorrow</string>
<!-- QuickSettings: Bluetooth auto on info text when disabled [CHAR LIMIT=NONE]-->
- <string name="turn_on_bluetooth_auto_info_disabled">Features like Quick Share, Find My Device, and device location use Bluetooth</string>
+ <string name="turn_on_bluetooth_auto_info_disabled">Features like Quick Share and Find My Device use Bluetooth</string>
<!-- QuickSettings: Bluetooth auto on info text when enabled [CHAR LIMIT=NONE]-->
- <string name="turn_on_bluetooth_auto_info_enabled">Bluetooth will turn on tomorrow at 5 AM</string>
+ <string name="turn_on_bluetooth_auto_info_enabled">Bluetooth will turn on tomorrow morning</string>
+ <!-- QuickSettings: Bluetooth dialog audio sharing button text [CHAR LIMIT=50]-->
+ <string name="quick_settings_bluetooth_audio_sharing_button">Audio Sharing</string>
+ <!-- QuickSettings: Bluetooth dialog audio sharing button text when sharing audio [CHAR LIMIT=50]-->
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing">Sharing Audio</string>
<!-- QuickSettings: Bluetooth secondary label for the battery level of a connected device [CHAR LIMIT=20]-->
<string name="quick_settings_bluetooth_secondary_label_battery_level"><xliff:g id="battery_level_as_percentage">%s</xliff:g> battery</string>
@@ -1128,6 +1134,9 @@
<!-- Indication on the keyguard that is shown when the device is dock charging. [CHAR LIMIT=80]-->
<string name="keyguard_indication_charging_time_dock"><xliff:g id="percentage" example="20%">%2$s</xliff:g> • Charging • Full in <xliff:g id="charging_time_left" example="4 hr, 2 min">%1$s</xliff:g></string>
+ <!-- Label for accessibility action that shows widgets on lock screen on click. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_open_communal_hub">Widgets on lock screen</string>
+
<!-- Indicator on keyguard to start the communal tutorial. [CHAR LIMIT=100] -->
<string name="communal_tutorial_indicator_text">Swipe left to start the communal tutorial</string>
@@ -1165,6 +1174,10 @@
<string name="work_mode_off_title">Unpause work apps?</string>
<!-- Title for button to unpause on work profile. [CHAR LIMIT=NONE] -->
<string name="work_mode_turn_on">Unpause</string>
+ <!-- Label for accessibility action that navigates to lock screen. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_label_close_communal_hub">Close widgets on lock screen</string>
+ <!-- Accessibility content description for communal hub. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_content_description_for_communal_hub">Widgets on lock screen</string>
<!-- Related to user switcher --><skip/>
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensor.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensor.kt
index a2b1198..f07dce5 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensor.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics.shared.model
+import android.graphics.Rect
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
/** Fingerprint sensor property. Represents [FingerprintSensorPropertiesInternal]. */
@@ -23,12 +24,23 @@
val sensorId: Int,
val sensorStrength: SensorStrength,
val maxEnrollmentsPerUser: Int,
- val sensorType: FingerprintSensorType
+ val sensorType: FingerprintSensorType,
+ val sensorBounds: Rect,
+ val sensorRadius: Int,
)
/** Convert [FingerprintSensorPropertiesInternal] to corresponding [FingerprintSensor] */
fun FingerprintSensorPropertiesInternal.toFingerprintSensor(): FingerprintSensor {
val sensorStrength: SensorStrength = this.sensorStrength.toSensorStrength()
val sensorType: FingerprintSensorType = this.sensorType.toSensorType()
- return FingerprintSensor(this.sensorId, sensorStrength, this.maxEnrollmentsPerUser, sensorType)
+ val sensorBounds: Rect = this.location.rect
+ val sensorRadius = this.location.sensorRadius
+ return FingerprintSensor(
+ this.sensorId,
+ sensorStrength,
+ this.maxEnrollmentsPerUser,
+ sensorType,
+ sensorBounds,
+ sensorRadius,
+ )
}
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
index 476daac..0f3d285 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
@@ -33,3 +33,10 @@
SensorProperties.STRENGTH_STRONG -> SensorStrength.STRONG
else -> throw IllegalArgumentException("Invalid SensorStrength value: $this")
}
+/** Convert [SensorStrength] to corresponding [Int] */
+fun SensorStrength.toInt(): Int =
+ when (this) {
+ SensorStrength.CONVENIENCE -> SensorProperties.STRENGTH_CONVENIENCE
+ SensorStrength.WEAK -> SensorProperties.STRENGTH_WEAK
+ SensorStrength.STRONG -> SensorProperties.STRENGTH_STRONG
+ }
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 70182c1..460779c 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -15,6 +15,7 @@
*/
package com.android.keyguard
+import android.os.Trace
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@@ -31,7 +32,6 @@
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.customization.R
import com.android.systemui.dagger.qualifiers.Background
@@ -66,7 +66,6 @@
import java.util.TimeZone
import java.util.concurrent.Executor
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
@@ -74,6 +73,7 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
/**
* Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
@@ -91,7 +91,6 @@
@DisplaySpecific private val resources: Resources,
private val context: Context,
@Main private val mainExecutor: DelayableExecutor,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
@Background private val bgExecutor: Executor,
private val clockBuffers: ClockMessageBuffers,
private val featureFlags: FeatureFlagsClassic,
@@ -426,12 +425,13 @@
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
zenModeController.addCallback(zenModeCallback)
disposableHandle =
- parent.repeatWhenAttached(mainImmediateDispatcher) {
+ parent.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
listenForDozing(this)
if (MigrateClocksToBlueprint.isEnabled) {
listenForDozeAmountTransition(this)
listenForAnyStateToAodTransition(this)
+ listenForAnyStateToLockscreenTransition(this)
} else {
listenForDozeAmount(this)
}
@@ -522,8 +522,12 @@
private fun handleDoze(doze: Float) {
dozeAmount = doze
clock?.run {
+ Trace.beginSection("$TAG#smallClock.animations.doze")
smallClock.animations.doze(dozeAmount)
+ Trace.endSection()
+ Trace.beginSection("$TAG#largeClock.animations.doze")
largeClock.animations.doze(dozeAmount)
+ Trace.endSection()
}
smallTimeListener?.update(doze < DOZE_TICKRATE_THRESHOLD)
largeTimeListener?.update(doze < DOZE_TICKRATE_THRESHOLD)
@@ -531,19 +535,17 @@
@VisibleForTesting
internal fun listenForDozeAmount(scope: CoroutineScope): Job {
- return scope.launch("$TAG#listenForDozeAmount") {
- keyguardInteractor.dozeAmount.collect { handleDoze(it) }
- }
+ return scope.launch { keyguardInteractor.dozeAmount.collect { handleDoze(it) } }
}
@VisibleForTesting
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
- return scope.launch("$TAG#listenForDozeAmountTransition") {
+ return scope.launch {
merge(
- keyguardTransitionInteractor.aodToLockscreenTransition.map { step ->
+ keyguardTransitionInteractor.transition(AOD, LOCKSCREEN).map { step ->
step.copy(value = 1f - step.value)
},
- keyguardTransitionInteractor.lockscreenToAodTransition,
+ keyguardTransitionInteractor.transition(LOCKSCREEN, AOD),
).filter {
it.transitionState != TransitionState.FINISHED
}
@@ -556,7 +558,7 @@
*/
@VisibleForTesting
internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
- return scope.launch("$TAG#listenForAnyStateToAodTransition") {
+ return scope.launch {
keyguardTransitionInteractor
.transitionStepsToState(AOD)
.filter { it.transitionState == TransitionState.STARTED }
@@ -566,8 +568,19 @@
}
@VisibleForTesting
+ internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job {
+ return scope.launch {
+ keyguardTransitionInteractor
+ .transitionStepsToState(LOCKSCREEN)
+ .filter { it.transitionState == TransitionState.STARTED }
+ .filter { it.from != AOD }
+ .collect { handleDoze(0f) }
+ }
+ }
+
+ @VisibleForTesting
internal fun listenForDozing(scope: CoroutineScope): Job {
- return scope.launch("$TAG#listenForDozing") {
+ return scope.launch {
combine(
keyguardInteractor.dozeAmount,
keyguardInteractor.isDozing,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 458a21c..75d925d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -107,7 +107,10 @@
}
}
- private void updateMessageAreaVisibility() {
+ /**
+ * Determines whether to show the message area controlled by MessageAreaController.
+ */
+ public void updateMessageAreaVisibility() {
if (mMessageAreaController == null) return;
if (Flags.revampedBouncerMessages()) {
mMessageAreaController.disable();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 25d7713..e8e1cab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -83,14 +83,14 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardWmStateRefactor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -101,6 +101,8 @@
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;
+import dagger.Lazy;
+
import java.io.File;
import java.util.Arrays;
import java.util.Optional;
@@ -108,7 +110,6 @@
import javax.inject.Inject;
import javax.inject.Provider;
-import dagger.Lazy;
import kotlinx.coroutines.Job;
/** Controller for {@link KeyguardSecurityContainer} */
@@ -133,7 +134,6 @@
private final UserSwitcherController mUserSwitcherController;
private final GlobalSettings mGlobalSettings;
private final FeatureFlags mFeatureFlags;
- private final SceneContainerFlags mSceneContainerFlags;
private final SessionTracker mSessionTracker;
private final Optional<SideFpsController> mSideFpsController;
private final FalsingA11yDelegate mFalsingA11yDelegate;
@@ -310,7 +310,7 @@
*/
@Override
public void finish(int targetUserId) {
- if (!mFeatureFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ if (!RefactorKeyguardDismissIntent.isEnabled()) {
// If there's a pending runnable because the user interacted with a widget
// and we're leaving keyguard, then run it.
boolean deferKeyguardDone = false;
@@ -455,7 +455,6 @@
FalsingManager falsingManager,
UserSwitcherController userSwitcherController,
FeatureFlags featureFlags,
- SceneContainerFlags sceneContainerFlags,
GlobalSettings globalSettings,
SessionTracker sessionTracker,
Optional<SideFpsController> sideFpsController,
@@ -490,7 +489,6 @@
mFalsingManager = falsingManager;
mUserSwitcherController = userSwitcherController;
mFeatureFlags = featureFlags;
- mSceneContainerFlags = sceneContainerFlags;
mGlobalSettings = globalSettings;
mSessionTracker = sessionTracker;
if (SideFpsControllerRefactor.isEnabled()) {
@@ -533,7 +531,7 @@
showPrimarySecurityScreen(false);
- if (mSceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
// When the scene framework says that the lockscreen has been dismissed, dismiss the
// keyguard here, revealing the underlying app or launcher:
mSceneTransitionCollectionJob = mJavaAdapter.get().alwaysCollectFlow(
@@ -649,7 +647,7 @@
* @param action callback to be invoked when keyguard disappear animation completes.
*/
public void setOnDismissAction(ActivityStarter.OnDismissAction action, Runnable cancelAction) {
- if (mFeatureFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ if (RefactorKeyguardDismissIntent.isEnabled()) {
return;
}
if (mCancelAction != null) {
@@ -943,7 +941,7 @@
mUiEventLogger.log(uiEvent, getSessionId());
}
- if (mFeatureFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ if (RefactorKeyguardDismissIntent.isEnabled()) {
if (authenticatedWithPrimaryAuth) {
mPrimaryBouncerInteractor.get()
.notifyKeyguardAuthenticatedPrimaryAuth(targetUserId);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 558679e..3ef3418 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -118,6 +118,12 @@
}
@Override
+ public void updateMessageAreaVisibility() {
+ if (mMessageAreaController == null) return;
+ mMessageAreaController.setIsVisible(true);
+ }
+
+ @Override
void resetState() {
super.resetState();
if (DEBUG) Log.v(TAG, "Resetting state");
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index cb1c4b3..46225c7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -115,6 +115,12 @@
}
@Override
+ public void updateMessageAreaVisibility() {
+ if (mMessageAreaController == null) return;
+ mMessageAreaController.setIsVisible(true);
+ }
+
+ @Override
public void onResume(int reason) {
super.onResume(reason);
if (mShowDefaultMessage) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index 9b09265..afeb0f8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -189,6 +189,5 @@
ShadeLockscreenInteractor shadeLockscreenInteractor,
@Nullable ShadeExpansionStateManager shadeExpansionStateManager,
BiometricUnlockController biometricUnlockController,
- View notificationContainer,
- KeyguardBypassController bypassController);
+ View notificationContainer);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
index 4e5df35..cf2675b 100644
--- a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
@@ -74,7 +74,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -131,7 +131,6 @@
@NonNull private final KeyguardInteractor mKeyguardInteractor;
@NonNull private final View.AccessibilityDelegate mAccessibilityDelegate;
@NonNull private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor;
- @NonNull private final SceneContainerFlags mSceneContainerFlags;
// Tracks the velocity of a touch to help filter out the touches that move too fast.
private VelocityTracker mVelocityTracker;
@@ -208,8 +207,7 @@
@NonNull FeatureFlags featureFlags,
PrimaryBouncerInteractor primaryBouncerInteractor,
Context context,
- Lazy<DeviceEntryInteractor> deviceEntryInteractor,
- SceneContainerFlags sceneContainerFlags
+ Lazy<DeviceEntryInteractor> deviceEntryInteractor
) {
mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -236,7 +234,6 @@
mResources = resources;
mContext = context;
mDeviceEntryInteractor = deviceEntryInteractor;
- mSceneContainerFlags = sceneContainerFlags;
mAccessibilityDelegate = new View.AccessibilityDelegate() {
private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
@@ -746,7 +743,7 @@
// play device entry haptic (consistent with UDFPS controller longpress)
vibrateOnLongPress();
- if (mSceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
mDeviceEntryInteractor.get().attemptDeviceEntry();
} else {
mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
index 5bd85a7..429d3f0 100644
--- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -14,6 +14,8 @@
package com.android.systemui;
+import static com.android.systemui.Flags.sliceBroadcastRelayInBackground;
+
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -23,13 +25,19 @@
import android.net.Uri;
import android.os.UserHandle;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.GuardedBy;
+import androidx.annotation.WorkerThread;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.SliceBroadcastRelay;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -42,14 +50,18 @@
private static final String TAG = "SliceBroadcastRelay";
private static final boolean DEBUG = false;
+ @GuardedBy("mRelays")
private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>();
private final Context mContext;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final Executor mBackgroundExecutor;
@Inject
- public SliceBroadcastRelayHandler(Context context, BroadcastDispatcher broadcastDispatcher) {
+ public SliceBroadcastRelayHandler(Context context, BroadcastDispatcher broadcastDispatcher,
+ @Background Executor backgroundExecutor) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
+ mBackgroundExecutor = backgroundExecutor;
}
@Override
@@ -57,21 +69,29 @@
if (DEBUG) Log.d(TAG, "Start");
IntentFilter filter = new IntentFilter(SliceBroadcastRelay.ACTION_REGISTER);
filter.addAction(SliceBroadcastRelay.ACTION_UNREGISTER);
- mBroadcastDispatcher.registerReceiver(mReceiver, filter);
+
+ if (sliceBroadcastRelayInBackground()) {
+ mBroadcastDispatcher.registerReceiver(mReceiver, filter, mBackgroundExecutor);
+ } else {
+ mBroadcastDispatcher.registerReceiver(mReceiver, filter);
+ }
}
// This does not use BroadcastDispatcher as the filter may have schemas or mime types.
+ @WorkerThread
@VisibleForTesting
void handleIntent(Intent intent) {
if (SliceBroadcastRelay.ACTION_REGISTER.equals(intent.getAction())) {
- Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI);
+ Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI, Uri.class);
ComponentName receiverClass =
- intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_RECEIVER);
- IntentFilter filter = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_FILTER);
+ intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
+ ComponentName.class);
+ IntentFilter filter = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_FILTER,
+ IntentFilter.class);
if (DEBUG) Log.d(TAG, "Register " + uri + " " + receiverClass + " " + filter);
getOrCreateRelay(uri).register(mContext, receiverClass, filter);
} else if (SliceBroadcastRelay.ACTION_UNREGISTER.equals(intent.getAction())) {
- Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI);
+ Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI, Uri.class);
if (DEBUG) Log.d(TAG, "Unregister " + uri);
BroadcastRelay relay = getAndRemoveRelay(uri);
if (relay != null) {
@@ -80,17 +100,23 @@
}
}
+ @WorkerThread
private BroadcastRelay getOrCreateRelay(Uri uri) {
- BroadcastRelay ret = mRelays.get(uri);
- if (ret == null) {
- ret = new BroadcastRelay(uri);
- mRelays.put(uri, ret);
+ synchronized (mRelays) {
+ BroadcastRelay ret = mRelays.get(uri);
+ if (ret == null) {
+ ret = new BroadcastRelay(uri);
+ mRelays.put(uri, ret);
+ }
+ return ret;
}
- return ret;
}
+ @WorkerThread
private BroadcastRelay getAndRemoveRelay(Uri uri) {
- return mRelays.remove(uri);
+ synchronized (mRelays) {
+ return mRelays.remove(uri);
+ }
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -102,7 +128,7 @@
private static class BroadcastRelay extends BroadcastReceiver {
- private final ArraySet<ComponentName> mReceivers = new ArraySet<>();
+ private final CopyOnWriteArraySet<ComponentName> mReceivers = new CopyOnWriteArraySet<>();
private final UserHandle mUserId;
private final Uri mUri;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 7e96e48..615363d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -72,6 +72,7 @@
private boolean mEndAnimationCanceled = false;
@MagnificationState
private int mState = STATE_DISABLED;
+ private Runnable mOnAnimationEndRunnable;
WindowMagnificationAnimationController(@UiContext Context context) {
this(context, newValueAnimator(context.getResources()));
@@ -303,12 +304,7 @@
return;
}
- // If the animation is playing backwards, mStartSpec will be the final spec we would
- // like to reach.
- AnimationSpec spec = isReverse ? mStartSpec : mEndSpec;
- mController.updateWindowMagnificationInternal(
- spec.mScale, spec.mCenterX, spec.mCenterY,
- mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY);
+ mOnAnimationEndRunnable.run();
if (mState == STATE_DISABLING) {
mController.deleteWindowMagnification();
@@ -333,6 +329,10 @@
public void onAnimationRepeat(Animator animation) {
}
+ void setOnAnimationEndRunnable(Runnable runnable) {
+ mOnAnimationEndRunnable = runnable;
+ }
+
private void sendAnimationCallback(boolean success) {
if (mAnimationCallback != null) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index a847c3d..9837e36 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -260,6 +260,11 @@
mContext = context;
mHandler = handler;
mAnimationController = animationController;
+ mAnimationController.setOnAnimationEndRunnable(() -> {
+ if (Flags.createWindowlessWindowMagnifier()) {
+ notifySourceBoundsChanged();
+ }
+ });
mAnimationController.setWindowMagnificationController(this);
mWindowMagnifierCallback = callback;
mSysUiState = sysUiState;
@@ -1051,11 +1056,15 @@
// Notify source bounds change when the magnifier is not animating.
if (!mAnimationController.isAnimating()) {
- mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, mSourceBounds);
+ notifySourceBoundsChanged();
}
}
}
+ private void notifySourceBoundsChanged() {
+ mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, mSourceBounds);
+ }
+
/**
* Updates the position of {@link mSurfaceControlViewHost} and layout params of MirrorView based
* on the position and size of {@link #mMagnificationFrame}.
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index 1018f70..eb840f1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -244,11 +244,13 @@
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
/* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
UserHandle.USER_CURRENT);
- mSecureSettings.registerContentObserverForUser(
- mSecureSettings.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
- /* notifyForDescendants */ false,
- mMenuTargetFeaturesContentObserver,
- UserHandle.USER_CURRENT);
+ if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+ mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
+ /* notifyForDescendants */ false,
+ mMenuTargetFeaturesContentObserver,
+ UserHandle.USER_CURRENT);
+ }
mSecureSettings.registerContentObserverForUser(
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
/* notifyForDescendants */ false, mMenuSizeContentObserver,
@@ -263,8 +265,10 @@
UserHandle.USER_CURRENT);
mContext.registerComponentCallbacks(mComponentCallbacks);
- mAccessibilityManager.addAccessibilityServicesStateChangeListener(
- mA11yServicesStateChangeListener);
+ if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+ mAccessibilityManager.addAccessibilityServicesStateChangeListener(
+ mA11yServicesStateChangeListener);
+ }
}
void unregisterObserversAndCallbacks() {
@@ -273,8 +277,10 @@
mContext.getContentResolver().unregisterContentObserver(mMenuFadeOutContentObserver);
mContext.unregisterComponentCallbacks(mComponentCallbacks);
- mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
- mA11yServicesStateChangeListener);
+ if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+ mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
+ mA11yServicesStateChangeListener);
+ }
}
interface OnSettingsContentsChanged {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index 7cb028a..b8ff0c0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.accessibility.qs
+import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tileimpl.QSTileImpl
@@ -40,9 +41,14 @@
import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor
import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor
import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
+import com.android.systemui.qs.tiles.impl.reducebrightness.domain.interactor.ReduceBrightColorsTileDataInteractor
+import com.android.systemui.qs.tiles.impl.reducebrightness.domain.interactor.ReduceBrightColorsTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.qs.tiles.impl.reducebrightness.ui.ReduceBrightColorsTileMapper
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -105,6 +111,7 @@
const val COLOR_CORRECTION_TILE_SPEC = "color_correction"
const val COLOR_INVERSION_TILE_SPEC = "inversion"
const val FONT_SCALING_TILE_SPEC = "font_scaling"
+ const val REDUCE_BRIGHTNESS_TILE_SPEC = "reduce_brightness"
@Provides
@IntoMap
@@ -198,5 +205,41 @@
stateInteractor,
mapper,
)
+
+ @Provides
+ @IntoMap
+ @StringKey(REDUCE_BRIGHTNESS_TILE_SPEC)
+ fun provideReduceBrightColorsTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+ QSTileConfig(
+ tileSpec = TileSpec.create(REDUCE_BRIGHTNESS_TILE_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = R.drawable.qs_extra_dim_icon_on,
+ labelRes = com.android.internal.R.string.reduce_bright_colors_feature_name,
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ )
+
+ /**
+ * Inject Reduce Bright Colors Tile into tileViewModelMap in QSModule. The tile is hidden
+ * behind a flag.
+ */
+ @Provides
+ @IntoMap
+ @StringKey(REDUCE_BRIGHTNESS_TILE_SPEC)
+ fun provideReduceBrightColorsTileViewModel(
+ factory: QSTileViewModelFactory.Static<ReduceBrightColorsTileModel>,
+ mapper: ReduceBrightColorsTileMapper,
+ stateInteractor: ReduceBrightColorsTileDataInteractor,
+ userActionInteractor: ReduceBrightColorsTileUserActionInteractor
+ ): QSTileViewModel =
+ if (Flags.qsNewTilesFuture())
+ factory.create(
+ TileSpec.create(REDUCE_BRIGHTNESS_TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
+ else StubQSTileViewModel
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ailabs/OWNERS b/packages/SystemUI/src/com/android/systemui/ailabs/OWNERS
new file mode 100644
index 0000000..b65d29c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ailabs/OWNERS
@@ -0,0 +1,9 @@
+# Bug component: 1495344
+
+dupin@google.com
+linyuh@google.com
+pauldpong@google.com
+praveenj@google.com
+vicliang@google.com
+mfolkerts@google.com
+yuklimko@google.com
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
similarity index 60%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
index f6140f5..ea00398 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
@@ -14,9 +14,15 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.ambient.dagger
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
+import com.android.systemui.ambient.touch.dagger.InputSessionComponent
+import dagger.Module
+
+@Module(subcomponents = [AmbientTouchComponent::class, InputSessionComponent::class])
+interface AmbientModule {
+ companion object {
+ const val TOUCH_HANDLERS = "touch_handlers"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java
index 75c50fd..d0f08f5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch;
-
-import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING;
-import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING;
-import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_START_REGION;
-import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.MIN_BOUNCER_ZONE_SCREEN_PERCENTAGE;
+package com.android.systemui.ambient.touch;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -37,9 +32,11 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.Flags;
+import com.android.systemui.ambient.touch.dagger.BouncerSwipeModule;
+import com.android.systemui.ambient.touch.scrim.ScrimController;
+import com.android.systemui.ambient.touch.scrim.ScrimManager;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
-import com.android.systemui.dreams.touch.scrim.ScrimController;
-import com.android.systemui.dreams.touch.scrim.ScrimManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -54,7 +51,7 @@
/**
* Monitor for tracking touches on the DreamOverlay to bring up the bouncer.
*/
-public class BouncerSwipeTouchHandler implements DreamTouchHandler {
+public class BouncerSwipeTouchHandler implements TouchHandler {
/**
* An interface for creating ValueAnimators.
*/
@@ -124,13 +121,19 @@
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
if (mCapture == null) {
- // If the user scrolling favors a vertical direction, begin capturing
- // scrolls.
- mCapture = Math.abs(distanceY) > Math.abs(distanceX);
mBouncerInitiallyShowing = mCentralSurfaces
.map(CentralSurfaces::isBouncerShowing)
.orElse(false);
+ if (Flags.dreamOverlayBouncerSwipeDirectionFiltering()) {
+ mCapture = Math.abs(distanceY) > Math.abs(distanceX)
+ && ((distanceY < 0 && mBouncerInitiallyShowing)
+ || (distanceY > 0 && !mBouncerInitiallyShowing));
+ } else {
+ // If the user scrolling favors a vertical direction, begin capturing
+ // scrolls.
+ mCapture = Math.abs(distanceY) > Math.abs(distanceX);
+ }
if (mCapture) {
// reset expanding
mExpanded = false;
@@ -219,12 +222,12 @@
VelocityTrackerFactory velocityTrackerFactory,
LockPatternUtils lockPatternUtils,
UserTracker userTracker,
- @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
+ @Named(BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
FlingAnimationUtils flingAnimationUtils,
- @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING)
+ @Named(BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING)
FlingAnimationUtils flingAnimationUtilsClosing,
- @Named(SWIPE_TO_BOUNCER_START_REGION) float swipeRegionPercentage,
- @Named(MIN_BOUNCER_ZONE_SCREEN_PERCENTAGE) float minRegionPercentage,
+ @Named(BouncerSwipeModule.SWIPE_TO_BOUNCER_START_REGION) float swipeRegionPercentage,
+ @Named(BouncerSwipeModule.MIN_BOUNCER_ZONE_SCREEN_PERCENTAGE) float minRegionPercentage,
UiEventLogger uiEventLogger) {
mCentralSurfaces = centralSurfaces;
mScrimManager = scrimManager;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/InputSession.java
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/InputSession.java
index e1d0339..6a76c87 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/InputSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch;
+package com.android.systemui.ambient.touch;
-import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.INPUT_SESSION_NAME;
-import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.PILFER_ON_GESTURE_CONSUME;
+import static com.android.systemui.ambient.touch.dagger.AmbientTouchModule.PILFER_ON_GESTURE_CONSUME;
import android.os.Looper;
import android.view.Choreographer;
import android.view.GestureDetector;
import android.view.MotionEvent;
-import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.Flags;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -42,26 +42,34 @@
private final InputChannelCompat.InputEventReceiver mInputEventReceiver;
private final GestureDetector mGestureDetector;
+ // Pilfering is a destructive operation. Once pilfering starts, the all events will be captured
+ // by the associated monitor. We track whether we're pilfering since initiating pilfering
+ // requires reaching out to the InputManagerService, which can be a heavy operation. This is
+ // especially costly if this is happening on a continuous stream of motion events.
+ private boolean mPilfering;
+
/**
* Default session constructor.
- * @param sessionName The session name that will be applied to the underlying
- * {@link InputMonitorCompat}.
+ * @param inputMonitor Input monitor to track input events.
+ * @param gestureDetector Gesture detector for detecting gestures.
* @param inputEventListener A listener to receive input events.
- * @param gestureListener A listener to receive gesture events.
+ * @param choreographer Choreographer to use with the input receiver.
+ * @param looper Looper to use with the input receiver
* @param pilferOnGestureConsume Whether touch events should be pilfered after a gesture has
* been consumed.
*/
@Inject
- public InputSession(@Named(INPUT_SESSION_NAME) String sessionName,
+ public InputSession(
+ InputMonitorCompat inputMonitor,
+ GestureDetector gestureDetector,
InputChannelCompat.InputEventListener inputEventListener,
- GestureDetector.OnGestureListener gestureListener,
- DisplayTracker displayTracker,
+ Choreographer choreographer,
+ @Main Looper looper,
@Named(PILFER_ON_GESTURE_CONSUME) boolean pilferOnGestureConsume) {
- mInputMonitor = new InputMonitorCompat(sessionName, displayTracker.getDefaultDisplayId());
- mGestureDetector = new GestureDetector(gestureListener);
+ mInputMonitor = inputMonitor;
+ mGestureDetector = gestureDetector;
- mInputEventReceiver = mInputMonitor.getInputReceiver(Looper.getMainLooper(),
- Choreographer.getInstance(),
+ mInputEventReceiver = mInputMonitor.getInputReceiver(looper, choreographer,
ev -> {
// Process event. Since sometimes input may be a prerequisite for some
// gesture logic, process input first.
@@ -69,7 +77,9 @@
if (ev instanceof MotionEvent
&& mGestureDetector.onTouchEvent((MotionEvent) ev)
- && pilferOnGestureConsume) {
+ && pilferOnGestureConsume
+ && !(mPilfering && Flags.dreamInputSessionPilferOnce())) {
+ mPilfering = true;
mInputMonitor.pilferPointers();
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
index e0bf52e..9ef9938 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch;
+package com.android.systemui.ambient.touch;
-import static com.android.systemui.dreams.touch.dagger.ShadeModule.NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT;
+import static com.android.systemui.ambient.touch.dagger.ShadeModule.NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT;
import android.graphics.Rect;
import android.graphics.Region;
@@ -35,7 +35,7 @@
* {@link ShadeTouchHandler} is responsible for handling swipe down gestures over dream
* to bring down the shade.
*/
-public class ShadeTouchHandler implements DreamTouchHandler {
+public class ShadeTouchHandler implements TouchHandler {
private final Optional<CentralSurfaces> mSurfaces;
private final ShadeViewController mShadeViewController;
private final int mInitiationHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchHandler.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/TouchHandler.java
index 1ec0008..190bc15 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch;
+package com.android.systemui.ambient.touch;
import android.graphics.Rect;
import android.graphics.Region;
@@ -25,24 +25,30 @@
import com.google.common.util.concurrent.ListenableFuture;
/**
- * The {@link DreamTouchHandler} interface provides a way for dream overlay components to observe
+ * The {@link TouchHandler} interface provides a way for dream overlay components to observe
* touch events and gestures with the ability to intercept the latter. Touch interaction sequences
* are abstracted as sessions. A session represents the time of first
- * {@code android.view.MotionEvent.ACTION_DOWN} event to the last {@link DreamTouchHandler}
+ * {@code android.view.MotionEvent.ACTION_DOWN} event to the last {@link TouchHandler}
* stopping interception of gestures. If no gesture is intercepted, the session continues
- * indefinitely. {@link DreamTouchHandler} have the ability to create a stack of sessions, which
+ * indefinitely. {@link TouchHandler} have the ability to create a stack of sessions, which
* allows for motion logic to be captured in modal states.
*/
-public interface DreamTouchHandler {
+public interface TouchHandler {
/**
- * A touch session captures the interaction surface of a {@link DreamTouchHandler}. Clients
+ * A touch session captures the interaction surface of a {@link TouchHandler}. Clients
* register listeners as desired to participate in motion/gesture callbacks.
*/
interface TouchSession {
interface Callback {
+ /**
+ * Invoked when the session has been removed.
+ */
void onRemoved();
}
+ /**
+ * Registers a callback to be notified when there are updates to the {@link TouchSession}.
+ */
void registerCallback(Callback callback);
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
index 3b22b31..e7e12ba 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch;
+package com.android.systemui.ambient.touch;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -37,10 +37,10 @@
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
+import com.android.systemui.ambient.touch.dagger.InputSessionComponent;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dreams.touch.dagger.InputSessionComponent;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.util.display.DisplayHelper;
@@ -58,12 +58,12 @@
import javax.inject.Inject;
/**
- * {@link DreamOverlayTouchMonitor} is responsible for monitoring touches and gestures over the
+ * {@link TouchMonitor} is responsible for monitoring touches and gestures over the
* dream overlay and redirecting them to a set of listeners. This monitor is in charge of figuring
* out when listeners are eligible for receiving touches and filtering the listener pool if
* touches are consumed.
*/
-public class DreamOverlayTouchMonitor {
+public class TouchMonitor {
// This executor is used to protect {@code mActiveTouchSessions} from being modified
// concurrently. Any operation that adds or removes values should use this executor.
public String TAG = "DreamOverlayTouchMonitor";
@@ -83,11 +83,10 @@
};
-
/**
* Adds a new {@link TouchSessionImpl} to participate in receiving future touches and gestures.
*/
- private ListenableFuture<DreamTouchHandler.TouchSession> push(
+ private ListenableFuture<TouchHandler.TouchSession> push(
TouchSessionImpl touchSessionImpl) {
return CallbackToFutureAdapter.getFuture(completer -> {
mMainExecutor.execute(() -> {
@@ -110,7 +109,7 @@
/**
* Removes a {@link TouchSessionImpl} from receiving further updates.
*/
- private ListenableFuture<DreamTouchHandler.TouchSession> pop(
+ private ListenableFuture<TouchHandler.TouchSession> pop(
TouchSessionImpl touchSessionImpl) {
return CallbackToFutureAdapter.getFuture(completer -> {
mMainExecutor.execute(() -> {
@@ -140,11 +139,11 @@
}
/**
- * {@link TouchSessionImpl} implements {@link DreamTouchHandler.TouchSession} for
- * {@link DreamOverlayTouchMonitor}. It enables the monitor to access the associated listeners
+ * {@link TouchSessionImpl} implements {@link TouchHandler.TouchSession} for
+ * {@link TouchMonitor}. It enables the monitor to access the associated listeners
* and provides the associated client with access to the monitor.
*/
- private static class TouchSessionImpl implements DreamTouchHandler.TouchSession {
+ private static class TouchSessionImpl implements TouchHandler.TouchSession {
private final HashSet<InputChannelCompat.InputEventListener> mEventListeners =
new HashSet<>();
private final HashSet<GestureDetector.OnGestureListener> mGestureListeners =
@@ -152,10 +151,10 @@
private final HashSet<Callback> mCallbacks = new HashSet<>();
private final TouchSessionImpl mPredecessor;
- private final DreamOverlayTouchMonitor mTouchMonitor;
+ private final TouchMonitor mTouchMonitor;
private final Rect mBounds;
- TouchSessionImpl(DreamOverlayTouchMonitor touchMonitor, Rect bounds,
+ TouchSessionImpl(TouchMonitor touchMonitor, Rect bounds,
TouchSessionImpl predecessor) {
mPredecessor = predecessor;
mTouchMonitor = touchMonitor;
@@ -179,12 +178,12 @@
}
@Override
- public ListenableFuture<DreamTouchHandler.TouchSession> push() {
+ public ListenableFuture<TouchHandler.TouchSession> push() {
return mTouchMonitor.push(this);
}
@Override
- public ListenableFuture<DreamTouchHandler.TouchSession> pop() {
+ public ListenableFuture<TouchHandler.TouchSession> pop() {
return mTouchMonitor.pop(this);
}
@@ -275,10 +274,10 @@
});
}
mCurrentInputSession = mInputSessionFactory.create(
- "dreamOverlay",
- mInputEventListener,
- mOnGestureListener,
- true)
+ "dreamOverlay",
+ mInputEventListener,
+ mOnGestureListener,
+ true)
.getInputSession();
}
@@ -323,21 +322,21 @@
private final HashSet<TouchSessionImpl> mActiveTouchSessions = new HashSet<>();
- private final Collection<DreamTouchHandler> mHandlers;
+ private final Collection<TouchHandler> mHandlers;
private final DisplayHelper mDisplayHelper;
private boolean mStopMonitoringPending;
private InputChannelCompat.InputEventListener mInputEventListener =
new InputChannelCompat.InputEventListener() {
- @Override
- public void onInputEvent(InputEvent ev) {
- // No Active sessions are receiving touches. Create sessions for each listener
- if (mActiveTouchSessions.isEmpty()) {
- final HashMap<DreamTouchHandler, DreamTouchHandler.TouchSession> sessionMap =
- new HashMap<>();
+ @Override
+ public void onInputEvent(InputEvent ev) {
+ // No Active sessions are receiving touches. Create sessions for each listener
+ if (mActiveTouchSessions.isEmpty()) {
+ final HashMap<TouchHandler, TouchHandler.TouchSession> sessionMap =
+ new HashMap<>();
- for (DreamTouchHandler handler : mHandlers) {
+ for (TouchHandler handler : mHandlers) {
if (!handler.isEnabled()) {
continue;
}
@@ -349,46 +348,49 @@
exclusionRect = getCurrentExclusionRect();
}
handler.getTouchInitiationRegion(
- maxBounds, initiationRegion, exclusionRect);
+ maxBounds, initiationRegion, exclusionRect);
- if (!initiationRegion.isEmpty()) {
- // Initiation regions require a motion event to determine pointer location
- // within the region.
- if (!(ev instanceof MotionEvent)) {
- continue;
+ if (!initiationRegion.isEmpty()) {
+ // Initiation regions require a motion event to determine pointer
+ // location
+ // within the region.
+ if (!(ev instanceof MotionEvent)) {
+ continue;
+ }
+
+ final MotionEvent motionEvent = (MotionEvent) ev;
+
+ // If the touch event is outside the region, then ignore.
+ if (!initiationRegion.contains(Math.round(motionEvent.getX()),
+ Math.round(motionEvent.getY()))) {
+ continue;
+ }
+ }
+
+ final TouchSessionImpl sessionStack = new TouchSessionImpl(
+ TouchMonitor.this, maxBounds, null);
+ mActiveTouchSessions.add(sessionStack);
+ sessionMap.put(handler, sessionStack);
}
- final MotionEvent motionEvent = (MotionEvent) ev;
-
- // If the touch event is outside the region, then ignore.
- if (!initiationRegion.contains(Math.round(motionEvent.getX()),
- Math.round(motionEvent.getY()))) {
- continue;
- }
+ // Informing handlers of new sessions is delayed until we have all
+ // created so the
+ // final session is correct.
+ sessionMap.forEach((dreamTouchHandler, touchSession)
+ -> dreamTouchHandler.onSessionStart(touchSession));
}
- final TouchSessionImpl sessionStack = new TouchSessionImpl(
- DreamOverlayTouchMonitor.this, maxBounds, null);
- mActiveTouchSessions.add(sessionStack);
- sessionMap.put(handler, sessionStack);
+ // Find active sessions and invoke on InputEvent.
+ mActiveTouchSessions.stream()
+ .map(touchSessionStack -> touchSessionStack.getEventListeners())
+ .flatMap(Collection::stream)
+ .forEach(inputEventListener -> inputEventListener.onInputEvent(ev));
}
- // Informing handlers of new sessions is delayed until we have all created so the
- // final session is correct.
- sessionMap.forEach((dreamTouchHandler, touchSession)
- -> dreamTouchHandler.onSessionStart(touchSession));
- }
-
- // Find active sessions and invoke on InputEvent.
- mActiveTouchSessions.stream()
- .map(touchSessionStack -> touchSessionStack.getEventListeners())
- .flatMap(Collection::stream)
- .forEach(inputEventListener -> inputEventListener.onInputEvent(ev));
- }
- private Rect getCurrentExclusionRect() {
- return mExclusionRect;
- }
- };
+ private Rect getCurrentExclusionRect() {
+ return mExclusionRect;
+ }
+ };
/**
* The {@link Evaluator} interface allows for callers to inspect a listener from the
@@ -401,71 +403,75 @@
private GestureDetector.OnGestureListener mOnGestureListener =
new GestureDetector.OnGestureListener() {
- private boolean evaluate(Evaluator evaluator) {
- final Set<TouchSessionImpl> consumingSessions = new HashSet<>();
+ private boolean evaluate(Evaluator evaluator) {
+ final Set<TouchSessionImpl> consumingSessions = new HashSet<>();
- // When a gesture is consumed, it is assumed that all touches for the current session
- // should be directed only to those TouchSessions until those sessions are popped. All
- // non-participating sessions are removed from receiving further updates with
- // {@link DreamOverlayTouchMonitor#isolate}.
- final boolean eventConsumed = mActiveTouchSessions.stream()
- .map(touchSession -> {
- boolean consume = touchSession.getGestureListeners()
- .stream()
- .map(listener -> evaluator.evaluate(listener))
- .anyMatch(consumed -> consumed);
+ // When a gesture is consumed, it is assumed that all touches for the current
+ // session
+ // should be directed only to those TouchSessions until those sessions are
+ // popped. All
+ // non-participating sessions are removed from receiving further updates with
+ // {@link DreamOverlayTouchMonitor#isolate}.
+ final boolean eventConsumed = mActiveTouchSessions.stream()
+ .map(touchSession -> {
+ boolean consume = touchSession.getGestureListeners()
+ .stream()
+ .map(listener -> evaluator.evaluate(listener))
+ .anyMatch(consumed -> consumed);
- if (consume) {
- consumingSessions.add(touchSession);
- }
- return consume;
- }).anyMatch(consumed -> consumed);
+ if (consume) {
+ consumingSessions.add(touchSession);
+ }
+ return consume;
+ }).anyMatch(consumed -> consumed);
- if (eventConsumed) {
- DreamOverlayTouchMonitor.this.isolate(consumingSessions);
- }
+ if (eventConsumed) {
+ TouchMonitor.this.isolate(consumingSessions);
+ }
- return eventConsumed;
- }
+ return eventConsumed;
+ }
- // This method is called for gesture events that cannot be consumed.
- private void observe(Consumer<GestureDetector.OnGestureListener> consumer) {
- mActiveTouchSessions.stream()
- .map(touchSession -> touchSession.getGestureListeners())
- .flatMap(Collection::stream)
- .forEach(listener -> consumer.accept(listener));
- }
+ // This method is called for gesture events that cannot be consumed.
+ private void observe(Consumer<GestureDetector.OnGestureListener> consumer) {
+ mActiveTouchSessions.stream()
+ .map(touchSession -> touchSession.getGestureListeners())
+ .flatMap(Collection::stream)
+ .forEach(listener -> consumer.accept(listener));
+ }
- @Override
- public boolean onDown(MotionEvent e) {
- return evaluate(listener -> listener.onDown(e));
- }
+ @Override
+ public boolean onDown(MotionEvent e) {
+ return evaluate(listener -> listener.onDown(e));
+ }
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- return evaluate(listener -> listener.onFling(e1, e2, velocityX, velocityY));
- }
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ return evaluate(listener -> listener.onFling(e1, e2, velocityX, velocityY));
+ }
- @Override
- public void onLongPress(MotionEvent e) {
- observe(listener -> listener.onLongPress(e));
- }
+ @Override
+ public void onLongPress(MotionEvent e) {
+ observe(listener -> listener.onLongPress(e));
+ }
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- return evaluate(listener -> listener.onScroll(e1, e2, distanceX, distanceY));
- }
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ return evaluate(listener -> listener.onScroll(e1, e2, distanceX, distanceY));
+ }
- @Override
- public void onShowPress(MotionEvent e) {
- observe(listener -> listener.onShowPress(e));
- }
+ @Override
+ public void onShowPress(MotionEvent e) {
+ observe(listener -> listener.onShowPress(e));
+ }
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- return evaluate(listener -> listener.onSingleTapUp(e));
- }
- };
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return evaluate(listener -> listener.onSingleTapUp(e));
+ }
+ };
private InputSessionComponent.Factory mInputSessionFactory;
private InputSession mCurrentInputSession;
@@ -474,25 +480,27 @@
/**
- * Designated constructor for {@link DreamOverlayTouchMonitor}
- * @param executor This executor will be used for maintaining the active listener list to avoid
- * concurrent modification.
- * @param lifecycle {@link DreamOverlayTouchMonitor} will listen to this lifecycle to determine
- * whether touch monitoring should be active.
+ * Designated constructor for {@link TouchMonitor}
+ *
+ * @param executor This executor will be used for maintaining the active listener
+ * list to avoid
+ * concurrent modification.
+ * @param lifecycle {@link TouchMonitor} will listen to this lifecycle to determine
+ * whether touch monitoring should be active.
* @param inputSessionFactory This factory will generate the {@link InputSession} requested by
* the monitor. Each session should be unique and valid when
* returned.
- * @param handlers This set represents the {@link DreamTouchHandler} instances that will
- * participate in touch handling.
+ * @param handlers This set represents the {@link TouchHandler} instances that will
+ * participate in touch handling.
*/
@Inject
- public DreamOverlayTouchMonitor(
+ public TouchMonitor(
@Main Executor executor,
@Background Executor backgroundExecutor,
Lifecycle lifecycle,
InputSessionComponent.Factory inputSessionFactory,
DisplayHelper displayHelper,
- Set<DreamTouchHandler> handlers,
+ Set<TouchHandler> handlers,
IWindowManager windowManagerService,
@DisplayId int displayId) {
mDisplayId = displayId;
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchComponent.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchComponent.kt
new file mode 100644
index 0000000..390e53b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchComponent.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.ambient.touch.dagger
+
+import androidx.lifecycle.LifecycleOwner
+import com.android.systemui.ambient.dagger.AmbientModule.Companion.TOUCH_HANDLERS
+import com.android.systemui.ambient.touch.TouchHandler
+import com.android.systemui.ambient.touch.TouchMonitor
+import dagger.BindsInstance
+import dagger.Subcomponent
+import javax.inject.Named
+
+/**
+ * {@link AmbientTouchComponent} can be used for setting up a touch environment over the entire
+ * display surface. This allows for implementing behaviors such as swiping up to bring up the
+ * bouncer.
+ */
+@Subcomponent(modules = [AmbientTouchModule::class, ShadeModule::class, BouncerSwipeModule::class])
+interface AmbientTouchComponent {
+ @Subcomponent.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance lifecycleOwner: LifecycleOwner,
+ @BindsInstance
+ @Named(TOUCH_HANDLERS)
+ touchHandlers: Set<@JvmSuppressWildcards TouchHandler>
+ ): AmbientTouchComponent
+ }
+
+ /** Builds a [TouchMonitor] */
+ fun getTouchMonitor(): TouchMonitor
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchModule.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchModule.kt
new file mode 100644
index 0000000..a4924d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchModule.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.ambient.touch.dagger
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import com.android.systemui.ambient.dagger.AmbientModule
+import com.android.systemui.ambient.touch.TouchHandler
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ElementsIntoSet
+import javax.inject.Named
+
+@Module
+interface AmbientTouchModule {
+ companion object {
+ @JvmStatic
+ @Provides
+ fun providesLifecycle(lifecycleOwner: LifecycleOwner): Lifecycle {
+ return lifecycleOwner.lifecycle
+ }
+
+ @Provides
+ @ElementsIntoSet
+ fun providesDreamTouchHandlers(
+ @Named(AmbientModule.TOUCH_HANDLERS)
+ touchHandlers: Set<@JvmSuppressWildcards TouchHandler>
+ ): Set<@JvmSuppressWildcards TouchHandler> {
+ return touchHandlers
+ }
+
+ const val INPUT_SESSION_NAME = "INPUT_SESSION_NAME"
+ const val PILFER_ON_GESTURE_CONSUME = "PILFER_ON_GESTURE_CONSUME"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/BouncerSwipeModule.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/BouncerSwipeModule.java
index a5db2ff..dac2d8e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/BouncerSwipeModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch.dagger;
+package com.android.systemui.ambient.touch.dagger;
import android.animation.ValueAnimator;
import android.content.res.Resources;
import android.util.TypedValue;
import android.view.VelocityTracker;
+import com.android.systemui.ambient.touch.BouncerSwipeTouchHandler;
+import com.android.systemui.ambient.touch.TouchHandler;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dreams.touch.BouncerSwipeTouchHandler;
-import com.android.systemui.dreams.touch.DreamTouchHandler;
import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeViewController;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -66,7 +66,7 @@
*/
@Provides
@IntoSet
- public static DreamTouchHandler providesBouncerSwipeTouchHandler(
+ public static TouchHandler providesBouncerSwipeTouchHandler(
BouncerSwipeTouchHandler touchHandler) {
return touchHandler;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/InputSessionComponent.java
similarity index 77%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/InputSessionComponent.java
index ad59a2e..203fb64 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/InputSessionComponent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,32 +14,35 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch.dagger;
+package com.android.systemui.ambient.touch.dagger;
-import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.INPUT_SESSION_NAME;
-import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.PILFER_ON_GESTURE_CONSUME;
+import static com.android.systemui.ambient.touch.dagger.AmbientTouchModule.INPUT_SESSION_NAME;
+import static com.android.systemui.ambient.touch.dagger.AmbientTouchModule.PILFER_ON_GESTURE_CONSUME;
import android.view.GestureDetector;
-import com.android.systemui.dreams.touch.InputSession;
+import com.android.systemui.ambient.touch.InputSession;
import com.android.systemui.shared.system.InputChannelCompat;
-import javax.inject.Named;
-
import dagger.BindsInstance;
import dagger.Subcomponent;
+import javax.inject.Named;
+
/**
* {@link InputSessionComponent} generates {@link InputSession} with specific instances bound for
* the session name and whether touches should be pilfered when consumed.
*/
-@Subcomponent
+@Subcomponent(
+ modules = { InputSessionModule.class }
+)
public interface InputSessionComponent {
/**
* Generates {@link InputSessionComponent}.
*/
@Subcomponent.Factory
interface Factory {
+ /** */
InputSessionComponent create(@Named(INPUT_SESSION_NAME) @BindsInstance String name,
@BindsInstance InputChannelCompat.InputEventListener inputEventListener,
@BindsInstance GestureDetector.OnGestureListener gestureListener,
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/InputSessionModule.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/InputSessionModule.java
new file mode 100644
index 0000000..99dbdee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/InputSessionModule.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.ambient.touch.dagger;
+
+import static com.android.systemui.ambient.touch.dagger.AmbientTouchModule.INPUT_SESSION_NAME;
+
+import android.view.GestureDetector;
+
+import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+import dagger.Module;
+import dagger.Provides;
+
+import javax.inject.Named;
+
+
+/**
+ * Module for providing dependencies to {@link com.android.systemui.dreams.touch.InputSession}.
+ */
+@Module
+public interface InputSessionModule {
+ /** */
+ @Provides
+ static InputMonitorCompat providesInputMonitorCompat(@Named(INPUT_SESSION_NAME) String name,
+ DisplayTracker displayTracker) {
+ return new InputMonitorCompat(name, displayTracker.getDefaultDisplayId());
+ }
+
+ /** */
+ @Provides
+ static GestureDetector providesGestureDetector(
+ android.view.GestureDetector.OnGestureListener gestureListener) {
+ return new GestureDetector(gestureListener);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/ShadeModule.java
similarity index 62%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/ShadeModule.java
index 0f08d37..bc2f354 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/ShadeModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch.dagger;
+package com.android.systemui.ambient.touch.dagger;
import android.content.res.Resources;
+import com.android.systemui.ambient.touch.ShadeTouchHandler;
+import com.android.systemui.ambient.touch.TouchHandler;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dreams.touch.CommunalTouchHandler;
-import com.android.systemui.dreams.touch.DreamTouchHandler;
-import com.android.systemui.dreams.touch.ShadeTouchHandler;
import com.android.systemui.res.R;
import dagger.Binds;
@@ -43,23 +42,14 @@
public static final String NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT =
"notification_shade_gesture_initiation_height";
- /** Width of swipe gesture edge to show communal hub. */
- public static final String COMMUNAL_GESTURE_INITIATION_WIDTH =
- "communal_gesture_initiation_width";
-
/**
* Provides {@link ShadeTouchHandler} to handle notification swipe down over dream.
*/
@Binds
@IntoSet
- public abstract DreamTouchHandler providesNotificationShadeTouchHandler(
+ public abstract TouchHandler providesNotificationShadeTouchHandler(
ShadeTouchHandler touchHandler);
- /** Provides {@link CommunalTouchHandler}. */
- @Binds
- @IntoSet
- public abstract DreamTouchHandler bindCommunalTouchHandler(CommunalTouchHandler touchHandler);
-
/**
* Provides the height of the gesture area for notification swipe down.
*/
@@ -69,12 +59,4 @@
return resources.getDimensionPixelSize(R.dimen.dream_overlay_status_bar_height);
}
- /**
- * Provides the width of the gesture area for swiping open communal hub.
- */
- @Provides
- @Named(COMMUNAL_GESTURE_INITIATION_WIDTH)
- public static int providesCommunalGestureInitiationWidth(@Main Resources resources) {
- return resources.getDimensionPixelSize(R.dimen.communal_gesture_initiation_width);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java
index 776b7bd..94c9982 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch.scrim;
+package com.android.systemui.ambient.touch.scrim;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimController.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerlessScrimController.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimController.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerlessScrimController.java
index 01e4d04..c453ddb 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerlessScrimController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch.scrim;
+package com.android.systemui.ambient.touch.scrim;
import android.os.PowerManager;
import android.os.SystemClock;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimController.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/ScrimController.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimController.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/ScrimController.java
index 61629ef..0054352 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/ScrimController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch.scrim;
+package com.android.systemui.ambient.touch.scrim;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimManager.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/ScrimManager.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimManager.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/ScrimManager.java
index 0d0dff6..676221d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimManager.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/ScrimManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch.scrim;
+package com.android.systemui.ambient.touch.scrim;
-import static com.android.systemui.dreams.touch.scrim.dagger.ScrimModule.BOUNCERLESS_SCRIM_CONTROLLER;
-import static com.android.systemui.dreams.touch.scrim.dagger.ScrimModule.BOUNCER_SCRIM_CONTROLLER;
+import static com.android.systemui.ambient.touch.scrim.dagger.ScrimModule.BOUNCERLESS_SCRIM_CONTROLLER;
+import static com.android.systemui.ambient.touch.scrim.dagger.ScrimModule.BOUNCER_SCRIM_CONTROLLER;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.policy.KeyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/dagger/ScrimModule.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/dagger/ScrimModule.java
similarity index 79%
rename from packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/dagger/ScrimModule.java
rename to packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/dagger/ScrimModule.java
index 4ad5161..b07029b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/dagger/ScrimModule.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/dagger/ScrimModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch.scrim.dagger;
+package com.android.systemui.ambient.touch.scrim.dagger;
-import com.android.systemui.dreams.touch.scrim.BouncerScrimController;
-import com.android.systemui.dreams.touch.scrim.BouncerlessScrimController;
-import com.android.systemui.dreams.touch.scrim.ScrimController;
+import com.android.systemui.ambient.touch.scrim.BouncerScrimController;
+import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
+import com.android.systemui.ambient.touch.scrim.ScrimController;
import dagger.Module;
import dagger.Provides;
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index 454ed27..a9f985f 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -36,7 +36,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.onSubscriberAdded
@@ -186,7 +186,6 @@
constructor(
@Application private val applicationScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
- flags: SceneContainerFlags,
private val clock: SystemClock,
private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>,
private val userRepository: UserRepository,
@@ -255,7 +254,7 @@
override val hasLockoutOccurred: StateFlow<Boolean> = _hasLockoutOccurred.asStateFlow()
init {
- if (flags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
// Hydrate failedAuthenticationAttempts initially and whenever the selected user
// changes.
applicationScope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 3a45db1..4a60d19 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -323,7 +323,13 @@
overlayParams = updatedOverlayParams
sensorBounds = updatedOverlayParams.sensorBounds
getTouchOverlay()?.let {
- windowManager.updateViewLayout(it, coreLayoutParams.updateDimensions(null))
+ if (addViewRunnable == null) {
+ // Only updateViewLayout if there's no pending view to add to WM.
+ // If there is a pending view, that means the view hasn't been added yet so there's
+ // no need to update any layouts. Instead the correct params will be used when the
+ // view is eventually added.
+ windowManager.updateViewLayout(it, coreLayoutParams.updateDimensions(null))
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index ec54e4ce..9816896 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -282,7 +282,8 @@
@VisibleForTesting
suspend fun listenForGoneToAodTransition(scope: CoroutineScope): Job {
return scope.launch {
- transitionInteractor.goneToAodTransition.collect { transitionStep ->
+ transitionInteractor.transition(KeyguardState.GONE, KeyguardState.AOD).collect {
+ transitionStep ->
view.onDozeAmountChanged(
transitionStep.value,
transitionStep.value,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
index 83b3380..1eef91d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
@@ -27,7 +27,8 @@
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
-private val SUPPORTED_ROTATIONS = setOf(Surface.ROTATION_90, Surface.ROTATION_270)
+private val SUPPORTED_ROTATIONS =
+ setOf(Surface.ROTATION_90, Surface.ROTATION_270, Surface.ROTATION_180)
/**
* TODO(b/259140693): Consider using an object pool of TouchProcessorResult to avoid allocations.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 76d46ed..072fe47 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -463,6 +463,23 @@
}
}
}
+
+ // Retry and confirmation when finger on sensor
+ launch {
+ combine(
+ viewModel.canTryAgainNow,
+ viewModel.hasFingerOnSensor,
+ viewModel.isPendingConfirmation,
+ ::Triple
+ )
+ .collect { (canRetry, fingerAcquired, pendingConfirmation) ->
+ if (canRetry && fingerAcquired) {
+ legacyCallback.onButtonTryAgain()
+ } else if (pendingConfirmation && fingerAcquired) {
+ viewModel.confirmAuthenticated()
+ }
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
index 66b7d7a..d9d3715 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
@@ -26,6 +26,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieOnCompositionLoadedListener
import com.android.settingslib.widget.LottieColorUtils
import com.android.systemui.Flags.constraintBp
import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel
@@ -77,6 +78,8 @@
}
launch {
+ var lottieOnCompositionLoadedListener: LottieOnCompositionLoadedListener? = null
+
combine(viewModel.activeAuthType, viewModel.iconSize, ::Pair).collect {
(activeAuthType, iconSize) ->
// Every time after bp shows, [isIconViewLoaded] is set to false in
@@ -94,10 +97,18 @@
* TODO(b/288175072): May be able to remove this once constraint
* layout is implemented
*/
- iconView.removeAllLottieOnCompositionLoadedListener()
- iconView.addLottieOnCompositionLoadedListener {
- promptViewModel.setIsIconViewLoaded(true)
+ if (lottieOnCompositionLoadedListener != null) {
+ iconView.removeLottieOnCompositionLoadedListener(
+ lottieOnCompositionLoadedListener!!
+ )
}
+ lottieOnCompositionLoadedListener =
+ LottieOnCompositionLoadedListener {
+ promptViewModel.setIsIconViewLoaded(true)
+ }
+ iconView.addLottieOnCompositionLoadedListener(
+ lottieOnCompositionLoadedListener!!
+ )
}
AuthType.Face -> {
/**
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 21ebff4..4e9acbd 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
@@ -22,6 +22,7 @@
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
+import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.biometrics.BiometricPrompt
import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.biometrics.PromptContentView
@@ -32,6 +33,7 @@
import com.android.systemui.Flags.constraintBp
import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
@@ -40,6 +42,7 @@
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.Job
@@ -66,6 +69,7 @@
promptSelectorInteractor: PromptSelectorInteractor,
@Application private val context: Context,
private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
+ private val biometricStatusInteractor: BiometricStatusInteractor,
private val udfpsUtils: UdfpsUtils
) {
/** The set of modalities available for this prompt */
@@ -185,6 +189,24 @@
/** Fingerprint sensor state. */
val fingerprintStartMode: Flow<FingerprintStartMode> = _fingerprintStartMode.asStateFlow()
+ /** Whether a finger has been acquired by the sensor */
+ // TODO(b/331948073): Add support for detecting SFPS finger without authentication running
+ val hasFingerBeenAcquired: Flow<Boolean> =
+ combine(biometricStatusInteractor.fingerprintAcquiredStatus, modalities) {
+ status,
+ modalities ->
+ modalities.hasSfps &&
+ status is AcquiredFingerprintAuthenticationStatus &&
+ status.acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START
+ }
+ .distinctUntilChanged()
+
+ /** Whether there is currently a finger on the sensor */
+ val hasFingerOnSensor: Flow<Boolean> =
+ combine(hasFingerBeenAcquired, _isOverlayTouched) { hasFingerBeenAcquired, overlayTouched ->
+ hasFingerBeenAcquired || overlayTouched
+ }
+
private val _forceLargeSize = MutableStateFlow(false)
private val _forceMediumSize = MutableStateFlow(false)
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogController.java b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogController.java
index 161458f..a90e60d 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogController.java
@@ -56,4 +56,22 @@
broadcastDialog.show();
}
}
+
+ /** Creates a [BroadcastDialog] for the user to switch broadcast or change the output device
+ *
+ * @param currentBroadcastAppName Indicates the APP name currently broadcasting
+ * @param outputPkgName Indicates the output media package name to be switched
+ * @param controller Indicates the dialog controller of the source view.
+ */
+ public void createBroadcastDialogWithController(
+ String currentBroadcastAppName, String outputPkgName,
+ DialogTransitionAnimator.Controller controller) {
+ SystemUIDialog broadcastDialog = mBroadcastDialogFactory.create(
+ currentBroadcastAppName, outputPkgName).createDialog();
+ if (controller != null) {
+ mDialogTransitionAnimator.show(broadcastDialog, controller);
+ } else {
+ broadcastDialog.show();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java
index 66aeda6..207f7db 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java
@@ -217,10 +217,13 @@
mSwitchBroadcast.setText(mContext.getString(
R.string.bt_le_audio_broadcast_dialog_switch_app, switchBroadcastApp), null);
mSwitchBroadcast.setOnClickListener((view) -> startSwitchBroadcast());
- changeOutput.setOnClickListener((view) -> {
- mMediaOutputDialogManager.createAndShow(mOutputPackageName, true, null);
- dialog.dismiss();
- });
+ changeOutput.setOnClickListener(
+ (view) -> {
+ // TODO: b/321969740 - Take the userHandle as a parameter and pass it through.
+ // The package name is not sufficient to unambiguously identify an app.
+ mMediaOutputDialogManager.createAndShow(mOutputPackageName, true, null, null);
+ dialog.dismiss();
+ });
cancelBtn.setOnClickListener((view) -> {
if (DEBUG) {
Log.d(TAG, "BroadcastDialog dismiss.");
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
new file mode 100644
index 0000000..e44f054
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.bluetooth.qsdialog
+
+import androidx.annotation.StringRes
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.stateIn
+
+internal sealed class AudioSharingButtonState {
+ object Gone : AudioSharingButtonState()
+ data class Visible(@StringRes val resId: Int) : AudioSharingButtonState()
+}
+
+/** Holds business logic for the audio sharing state. */
+@SysUISingleton
+internal class AudioSharingInteractor
+@Inject
+constructor(
+ private val localBluetoothManager: LocalBluetoothManager?,
+ bluetoothStateInteractor: BluetoothStateInteractor,
+ deviceItemInteractor: DeviceItemInteractor,
+ @Application private val coroutineScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+ /** Flow representing the update of AudioSharingButtonState. */
+ internal val audioSharingButtonStateUpdate: Flow<AudioSharingButtonState> =
+ combine(
+ bluetoothStateInteractor.bluetoothStateUpdate,
+ deviceItemInteractor.deviceItemUpdate
+ ) { bluetoothState, deviceItem ->
+ getButtonState(bluetoothState, deviceItem)
+ }
+ .flowOn(backgroundDispatcher)
+ .stateIn(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(replayExpirationMillis = 0),
+ initialValue = AudioSharingButtonState.Gone
+ )
+
+ private fun getButtonState(
+ bluetoothState: Boolean,
+ deviceItem: List<DeviceItem>
+ ): AudioSharingButtonState {
+ return when {
+ // Don't show button when bluetooth is off
+ !bluetoothState -> AudioSharingButtonState.Gone
+ // Show sharing audio when broadcasting
+ BluetoothUtils.isBroadcasting(localBluetoothManager) ->
+ AudioSharingButtonState.Visible(
+ R.string.quick_settings_bluetooth_audio_sharing_button_sharing
+ )
+ // When not broadcasting, don't show button if there's connected source in any device
+ deviceItem.any {
+ BluetoothUtils.hasConnectedBroadcastSource(
+ it.cachedBluetoothDevice,
+ localBluetoothManager
+ )
+ } -> AudioSharingButtonState.Gone
+ // Show audio sharing when there's a connected LE audio device
+ deviceItem.any { BluetoothUtils.isActiveLeAudioDevice(it.cachedBluetoothDevice) } ->
+ AudioSharingButtonState.Visible(
+ R.string.quick_settings_bluetooth_audio_sharing_button
+ )
+ else -> AudioSharingButtonState.Gone
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt
index 94d7af7..17f9e63 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt
@@ -25,12 +25,17 @@
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
/** Holds business logic for the Bluetooth Dialog's bluetooth and device connection state */
@SysUISingleton
@@ -40,9 +45,10 @@
private val localBluetoothManager: LocalBluetoothManager?,
private val logger: BluetoothTileDialogLogger,
@Application private val coroutineScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
) {
- internal val bluetoothStateUpdate: StateFlow<Boolean?> =
+ internal val bluetoothStateUpdate: StateFlow<Boolean> =
conflatedCallbackFlow {
val listener =
object : BluetoothCallback {
@@ -64,16 +70,22 @@
localBluetoothManager?.eventManager?.registerCallback(listener)
awaitClose { localBluetoothManager?.eventManager?.unregisterCallback(listener) }
}
+ .onStart { emit(isBluetoothEnabled()) }
+ .flowOn(backgroundDispatcher)
.stateIn(
coroutineScope,
SharingStarted.WhileSubscribed(replayExpirationMillis = 0),
- initialValue = null
+ initialValue = false
)
- internal var isBluetoothEnabled: Boolean
- get() = localBluetoothManager?.bluetoothAdapter?.isEnabled == true
- set(value) {
- if (isBluetoothEnabled != value) {
+ suspend fun isBluetoothEnabled(): Boolean =
+ withContext(backgroundDispatcher) {
+ localBluetoothManager?.bluetoothAdapter?.isEnabled == true
+ }
+
+ suspend fun setBluetoothEnabled(value: Boolean) {
+ withContext(backgroundDispatcher) {
+ if (isBluetoothEnabled() != value) {
localBluetoothManager?.bluetoothAdapter?.apply {
if (value) enable() else disable()
logger.logBluetoothState(
@@ -83,6 +95,7 @@
}
}
}
+ }
companion object {
private const val TAG = "BtStateInteractor"
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
index c7d171d..dd8c0df 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
@@ -27,6 +27,7 @@
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
+import android.widget.Button
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.Switch
@@ -59,7 +60,6 @@
internal constructor(
@Assisted private val initialUiProperties: BluetoothTileDialogViewModel.UiProperties,
@Assisted private val cachedContentHeight: Int,
- @Assisted private val bluetoothToggleInitialValue: Boolean,
@Assisted private val bluetoothTileDialogCallback: BluetoothTileDialogCallback,
@Assisted private val dismissListener: Runnable,
@Main private val mainDispatcher: CoroutineDispatcher,
@@ -69,8 +69,7 @@
private val systemuiDialogFactory: SystemUIDialog.Factory,
) : SystemUIDialog.Delegate {
- private val mutableBluetoothStateToggle: MutableStateFlow<Boolean> =
- MutableStateFlow(bluetoothToggleInitialValue)
+ private val mutableBluetoothStateToggle: MutableStateFlow<Boolean?> = MutableStateFlow(null)
internal val bluetoothStateToggle
get() = mutableBluetoothStateToggle.asStateFlow()
@@ -99,7 +98,6 @@
fun create(
initialUiProperties: BluetoothTileDialogViewModel.UiProperties,
cachedContentHeight: Int,
- bluetoothEnabled: Boolean,
dialogCallback: BluetoothTileDialogCallback,
dimissListener: Runnable
): BluetoothTileDialogDelegate
@@ -130,6 +128,9 @@
getPairNewDeviceButton(dialog).setOnClickListener {
bluetoothTileDialogCallback.onPairNewDeviceClicked(it)
}
+ getAudioSharingButtonView(dialog).setOnClickListener {
+ bluetoothTileDialogCallback.onAudioSharingButtonClicked(it)
+ }
getScrollViewContent(dialog).apply {
minimumHeight =
resources.getDimensionPixelSize(initialUiProperties.scrollViewMinHeightResId)
@@ -211,9 +212,19 @@
getAutoOnToggleInfoTextView(dialog).text = dialog.context.getString(infoResId)
}
+ internal fun onAudioSharingButtonUpdated(
+ dialog: SystemUIDialog,
+ visibility: Int,
+ label: String?
+ ) {
+ getAudioSharingButtonView(dialog).apply {
+ this.visibility = visibility
+ label?.let { text = it }
+ }
+ }
+
private fun setupToggle(dialog: SystemUIDialog) {
val toggleView = getToggleView(dialog)
- toggleView.isChecked = bluetoothToggleInitialValue
toggleView.setOnCheckedChangeListener { view, isChecked ->
mutableBluetoothStateToggle.value = isChecked
view.apply {
@@ -259,6 +270,10 @@
return dialog.requireViewById(R.id.bluetooth_auto_on_toggle)
}
+ private fun getAudioSharingButtonView(dialog: SystemUIDialog): Button {
+ return dialog.requireViewById(R.id.audio_sharing_button)
+ }
+
private fun getAutoOnToggleView(dialog: SystemUIDialog): View {
return dialog.requireViewById(R.id.bluetooth_auto_on_toggle_layout)
}
@@ -412,6 +427,8 @@
const val ACTION_PREVIOUSLY_CONNECTED_DEVICE =
"com.android.settings.PREVIOUSLY_CONNECTED_DEVICE"
const val ACTION_PAIR_NEW_DEVICE = "android.settings.BLUETOOTH_PAIRING_SETTINGS"
+ const val ACTION_AUDIO_SHARING =
+ "com.google.android.settings.BLUETOOTH_AUDIO_SHARING_SETTINGS"
const val DISABLED_ALPHA = 0.3f
const val ENABLED_ALPHA = 1f
const val PROGRESS_BAR_ANIMATION_DURATION_MS = 1500L
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
index add1647..b592b8e 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
@@ -30,9 +30,12 @@
@UiEvent(doc = "Connected device clicked to active") CONNECTED_DEVICE_SET_ACTIVE(1499),
@UiEvent(doc = "Saved clicked to connect") SAVED_DEVICE_CONNECT(1500),
@UiEvent(doc = "Active device clicked to disconnect") ACTIVE_DEVICE_DISCONNECT(1507),
+ @UiEvent(doc = "Audio sharing device clicked, do nothing") AUDIO_SHARING_DEVICE_CLICKED(1699),
@UiEvent(doc = "Connected other device clicked to disconnect")
CONNECTED_OTHER_DEVICE_DISCONNECT(1508),
- @UiEvent(doc = "The auto on toggle is clicked") BLUETOOTH_AUTO_ON_TOGGLE_CLICKED(1617);
+ @UiEvent(doc = "The auto on toggle is clicked") BLUETOOTH_AUTO_ON_TOGGLE_CLICKED(1617),
+ @UiEvent(doc = "The audio sharing button is clicked")
+ BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700);
override fun getId() = metricId
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
index e65b657..eb919e3 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
@@ -28,9 +28,11 @@
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.systemui.Prefs
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_AUDIO_SHARING
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_BLUETOOTH_DEVICE_DETAILS
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_PAIR_NEW_DEVICE
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_PREVIOUSLY_CONNECTED_DEVICE
@@ -61,6 +63,7 @@
private val deviceItemInteractor: DeviceItemInteractor,
private val bluetoothStateInteractor: BluetoothStateInteractor,
private val bluetoothAutoOnInteractor: BluetoothAutoOnInteractor,
+ private val audioSharingInteractor: AudioSharingInteractor,
private val dialogTransitionAnimator: DialogTransitionAnimator,
private val activityStarter: ActivityStarter,
private val uiEventLogger: UiEventLogger,
@@ -119,7 +122,8 @@
dialog,
it.take(MAX_DEVICE_ITEM_ENTRY),
showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
- showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled
+ showPairNewDevice =
+ bluetoothStateInteractor.isBluetoothEnabled()
)
animateProgressBar(dialog, false)
}
@@ -142,10 +146,25 @@
}
.launchIn(this)
+ if (BluetoothUtils.isAudioSharingEnabled()) {
+ audioSharingInteractor.audioSharingButtonStateUpdate
+ .onEach {
+ if (it is AudioSharingButtonState.Visible) {
+ dialogDelegate.onAudioSharingButtonUpdated(
+ dialog,
+ VISIBLE,
+ context.getString(it.resId)
+ )
+ } else {
+ dialogDelegate.onAudioSharingButtonUpdated(dialog, GONE, null)
+ }
+ }
+ .launchIn(this)
+ }
+
// bluetoothStateUpdate is emitted when bluetooth on/off state is changed, re-fetch
// the device item list.
bluetoothStateInteractor.bluetoothStateUpdate
- .filterNotNull()
.onEach {
dialogDelegate.onBluetoothStateUpdated(
dialog,
@@ -165,9 +184,10 @@
// bluetoothStateToggle is emitted when user toggles the bluetooth state switch,
// send the new value to the bluetoothStateInteractor and animate the progress bar.
dialogDelegate.bluetoothStateToggle
+ .filterNotNull()
.onEach {
dialogDelegate.animateProgressBar(dialog, true)
- bluetoothStateInteractor.isBluetoothEnabled = it
+ bluetoothStateInteractor.setBluetoothEnabled(it)
}
.launchIn(this)
@@ -222,11 +242,10 @@
return bluetoothDialogDelegateFactory.create(
UiProperties.build(
- bluetoothStateInteractor.isBluetoothEnabled,
+ bluetoothStateInteractor.isBluetoothEnabled(),
isAutoOnToggleFeatureAvailable()
),
cachedContentHeight,
- bluetoothStateInteractor.isBluetoothEnabled,
this@BluetoothTileDialogViewModel,
{ cancelJob() }
)
@@ -256,6 +275,11 @@
startSettingsActivity(Intent(ACTION_PAIR_NEW_DEVICE), view)
}
+ override fun onAudioSharingButtonClicked(view: View) {
+ uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED)
+ startSettingsActivity(Intent(ACTION_AUDIO_SHARING), view)
+ }
+
private fun cancelJob() {
job?.cancel()
job = null
@@ -312,4 +336,5 @@
fun onDeviceItemGearClicked(deviceItem: DeviceItem, view: View)
fun onSeeAllClicked(view: View)
fun onPairNewDeviceClicked(view: View)
+ fun onAudioSharingButtonClicked(view: View)
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt
index dc5efef..0ea98d1 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt
@@ -37,6 +37,7 @@
enum class DeviceItemType {
ACTIVE_MEDIA_BLUETOOTH_DEVICE,
+ AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
CONNECTED_BLUETOOTH_DEVICE,
SAVED_BLUETOOTH_DEVICE,
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
index f04ba75..49d0847 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
@@ -21,13 +21,16 @@
import android.media.AudioManager
import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.flags.Flags
+import com.android.settingslib.flags.Flags.enableLeAudioSharing
import com.android.systemui.res.R
private val backgroundOn = R.drawable.settingslib_switch_bar_bg_on
private val backgroundOff = R.drawable.bluetooth_tile_dialog_bg_off
private val backgroundOffBusy = R.drawable.bluetooth_tile_dialog_bg_off_busy
private val connected = R.string.quick_settings_bluetooth_device_connected
+private val audioSharing = R.string.quick_settings_bluetooth_device_audio_sharing
private val saved = R.string.quick_settings_bluetooth_device_saved
private val actionAccessibilityLabelActivate =
R.string.accessibility_quick_settings_bluetooth_device_tap_to_activate
@@ -39,35 +42,81 @@
abstract fun isFilterMatched(
context: Context,
cachedDevice: CachedBluetoothDevice,
- audioManager: AudioManager?
+ audioManager: AudioManager,
): Boolean
abstract fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem
+
+ companion object {
+ @JvmStatic
+ fun createDeviceItem(
+ context: Context,
+ cachedDevice: CachedBluetoothDevice,
+ type: DeviceItemType,
+ connectionSummary: String,
+ background: Int,
+ actionAccessibilityLabel: String
+ ): DeviceItem {
+ return DeviceItem(
+ type = type,
+ cachedBluetoothDevice = cachedDevice,
+ deviceName = cachedDevice.name,
+ connectionSummary = connectionSummary,
+ iconWithDescription =
+ BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let {
+ Pair(it.first, it.second)
+ },
+ background = background,
+ isEnabled = !cachedDevice.isBusy,
+ actionAccessibilityLabel = actionAccessibilityLabel
+ )
+ }
+ }
}
internal open class ActiveMediaDeviceItemFactory : DeviceItemFactory() {
override fun isFilterMatched(
context: Context,
cachedDevice: CachedBluetoothDevice,
- audioManager: AudioManager?
+ audioManager: AudioManager
): Boolean {
return BluetoothUtils.isActiveMediaDevice(cachedDevice) &&
BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager)
}
override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
- return DeviceItem(
- type = DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE,
- cachedBluetoothDevice = cachedDevice,
- deviceName = cachedDevice.name,
- connectionSummary = cachedDevice.connectionSummary ?: "",
- iconWithDescription =
- BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
- Pair(p.first, p.second)
- },
- background = backgroundOn,
- isEnabled = !cachedDevice.isBusy,
- actionAccessibilityLabel = context.getString(actionAccessibilityLabelDisconnect),
+ return createDeviceItem(
+ context,
+ cachedDevice,
+ DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE,
+ cachedDevice.connectionSummary ?: "",
+ backgroundOn,
+ context.getString(actionAccessibilityLabelDisconnect)
+ )
+ }
+}
+
+internal class AudioSharingMediaDeviceItemFactory(
+ private val localBluetoothManager: LocalBluetoothManager?
+) : DeviceItemFactory() {
+ override fun isFilterMatched(
+ context: Context,
+ cachedDevice: CachedBluetoothDevice,
+ audioManager: AudioManager
+ ): Boolean {
+ return enableLeAudioSharing() &&
+ BluetoothUtils.hasConnectedBroadcastSource(cachedDevice, localBluetoothManager)
+ }
+
+ override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
+ return createDeviceItem(
+ context,
+ cachedDevice,
+ DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
+ cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
+ ?: context.getString(audioSharing),
+ if (cachedDevice.isBusy) backgroundOffBusy else backgroundOn,
+ ""
)
}
}
@@ -76,7 +125,7 @@
override fun isFilterMatched(
context: Context,
cachedDevice: CachedBluetoothDevice,
- audioManager: AudioManager?
+ audioManager: AudioManager
): Boolean {
return BluetoothUtils.isActiveMediaDevice(cachedDevice) &&
BluetoothUtils.isAvailableHearingDevice(cachedDevice)
@@ -87,27 +136,21 @@
override fun isFilterMatched(
context: Context,
cachedDevice: CachedBluetoothDevice,
- audioManager: AudioManager?
+ audioManager: AudioManager
): Boolean {
return !BluetoothUtils.isActiveMediaDevice(cachedDevice) &&
BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager)
}
- // TODO(b/298124674): move create() to the abstract class to reduce duplicate code
override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
- return DeviceItem(
- type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
- cachedBluetoothDevice = cachedDevice,
- deviceName = cachedDevice.name,
- connectionSummary = cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
- ?: context.getString(connected),
- iconWithDescription =
- BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
- Pair(p.first, p.second)
- },
- background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
- isEnabled = !cachedDevice.isBusy,
- actionAccessibilityLabel = context.getString(actionAccessibilityLabelActivate),
+ return createDeviceItem(
+ context,
+ cachedDevice,
+ DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
+ cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
+ ?: context.getString(connected),
+ if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
+ context.getString(actionAccessibilityLabelActivate)
)
}
}
@@ -116,7 +159,7 @@
override fun isFilterMatched(
context: Context,
cachedDevice: CachedBluetoothDevice,
- audioManager: AudioManager?
+ audioManager: AudioManager
): Boolean {
return !BluetoothUtils.isActiveMediaDevice(cachedDevice) &&
BluetoothUtils.isAvailableHearingDevice(cachedDevice)
@@ -127,32 +170,25 @@
override fun isFilterMatched(
context: Context,
cachedDevice: CachedBluetoothDevice,
- audioManager: AudioManager?
+ audioManager: AudioManager
): Boolean {
return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) {
- !BluetoothUtils.isExclusivelyManagedBluetoothDevice(
- context,
- cachedDevice.getDevice()
- ) && BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, audioManager)
+ !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) &&
+ BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, audioManager)
} else {
BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, audioManager)
}
}
override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
- return DeviceItem(
- type = DeviceItemType.CONNECTED_BLUETOOTH_DEVICE,
- cachedBluetoothDevice = cachedDevice,
- deviceName = cachedDevice.name,
- connectionSummary = cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
- ?: context.getString(connected),
- iconWithDescription =
- BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
- Pair(p.first, p.second)
- },
- background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
- isEnabled = !cachedDevice.isBusy,
- actionAccessibilityLabel = context.getString(actionAccessibilityLabelDisconnect),
+ return createDeviceItem(
+ context,
+ cachedDevice,
+ DeviceItemType.CONNECTED_BLUETOOTH_DEVICE,
+ cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
+ ?: context.getString(connected),
+ if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
+ context.getString(actionAccessibilityLabelDisconnect)
)
}
}
@@ -161,32 +197,26 @@
override fun isFilterMatched(
context: Context,
cachedDevice: CachedBluetoothDevice,
- audioManager: AudioManager?
+ audioManager: AudioManager
): Boolean {
return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) {
- !BluetoothUtils.isExclusivelyManagedBluetoothDevice(
- context,
- cachedDevice.getDevice()
- ) && cachedDevice.bondState == BluetoothDevice.BOND_BONDED && !cachedDevice.isConnected
+ !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) &&
+ cachedDevice.bondState == BluetoothDevice.BOND_BONDED &&
+ !cachedDevice.isConnected
} else {
cachedDevice.bondState == BluetoothDevice.BOND_BONDED && !cachedDevice.isConnected
}
}
override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
- return DeviceItem(
- type = DeviceItemType.SAVED_BLUETOOTH_DEVICE,
- cachedBluetoothDevice = cachedDevice,
- deviceName = cachedDevice.name,
- connectionSummary = cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
- ?: context.getString(saved),
- iconWithDescription =
- BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
- Pair(p.first, p.second)
- },
- background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
- isEnabled = !cachedDevice.isBusy,
- actionAccessibilityLabel = context.getString(actionAccessibilityLabelActivate),
+ return createDeviceItem(
+ context,
+ cachedDevice,
+ DeviceItemType.SAVED_BLUETOOTH_DEVICE,
+ cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
+ ?: context.getString(saved),
+ if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
+ context.getString(actionAccessibilityLabelActivate)
)
}
}
@@ -195,7 +225,7 @@
override fun isFilterMatched(
context: Context,
cachedDevice: CachedBluetoothDevice,
- audioManager: AudioManager?
+ audioManager: AudioManager
): Boolean {
return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) {
!BluetoothUtils.isExclusivelyManagedBluetoothDevice(
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
index 4e28caf..66e593b 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
@@ -113,6 +113,7 @@
private var deviceItemFactoryList: List<DeviceItemFactory> =
listOf(
ActiveMediaDeviceItemFactory(),
+ AudioSharingMediaDeviceItemFactory(localBluetoothManager),
AvailableMediaDeviceItemFactory(),
ConnectedDeviceItemFactory(),
SavedDeviceItemFactory()
@@ -121,6 +122,7 @@
private var displayPriority: List<DeviceItemType> =
listOf(
DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE,
+ DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
DeviceItemType.CONNECTED_BLUETOOTH_DEVICE,
DeviceItemType.SAVED_BLUETOOTH_DEVICE,
@@ -177,6 +179,9 @@
disconnect()
uiEventLogger.log(BluetoothTileDialogUiEvent.ACTIVE_DEVICE_DISCONNECT)
}
+ DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> {
+ uiEventLogger.log(BluetoothTileDialogUiEvent.AUDIO_SHARING_DEVICE_CLICKED)
+ }
DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
setActive()
uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index 7525ce0..fa19bf4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
@@ -27,9 +27,9 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.kotlin.BooleanFlowOperators.or
import com.android.systemui.util.time.SystemClock
import dagger.Lazy
import javax.inject.Inject
@@ -78,15 +78,14 @@
bouncerRepository.alternateBouncerUIAvailable
}
private val isDozingOrAod: Flow<Boolean> =
- keyguardTransitionInteractor
- .get()
- .transitions
- .map {
- it.to == KeyguardState.DOZING ||
- it.to == KeyguardState.AOD ||
- ((it.from == KeyguardState.DOZING || it.from == KeyguardState.AOD) &&
- it.transitionState != TransitionState.FINISHED)
- }
+ or(
+ keyguardTransitionInteractor.get().transitionValue(KeyguardState.DOZING).map {
+ it > 0f
+ },
+ keyguardTransitionInteractor.get().transitionValue(KeyguardState.AOD).map {
+ it > 0f
+ },
+ )
.distinctUntilChanged()
/**
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 aeb564d5..dd71bc7 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
@@ -16,16 +16,26 @@
package com.android.systemui.bouncer.domain.interactor
+import android.app.StatusBarManager.SESSION_KEYGUARD
+import com.android.compose.animation.scene.SceneKey
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
import com.android.systemui.bouncer.data.repository.BouncerRepository
+import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
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.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.log.SessionTracker
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
@@ -47,6 +57,9 @@
private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
private val falsingInteractor: FalsingInteractor,
private val powerInteractor: PowerInteractor,
+ private val uiEventLogger: UiEventLogger,
+ private val sessionTracker: SessionTracker,
+ sceneInteractor: SceneInteractor,
) {
private val _onIncorrectBouncerInput = MutableSharedFlow<Unit>()
val onIncorrectBouncerInput: SharedFlow<Unit> = _onIncorrectBouncerInput
@@ -80,6 +93,10 @@
}
.map {}
+ /** The scene to show when bouncer is dismissed. */
+ val dismissDestination: Flow<SceneKey> =
+ sceneInteractor.previousScene.map { it ?: Scenes.Lockscreen }
+
/** Notifies that the user has places down a pointer, not necessarily dragging just yet. */
fun onDown() {
falsingInteractor.avoidGesture()
@@ -154,6 +171,18 @@
) {
_onIncorrectBouncerInput.emit(Unit)
}
+
+ if (authenticationInteractor.getAuthenticationMethod() in setOf(Pin, Password, Pattern)) {
+ if (authResult == AuthenticationResult.SUCCEEDED) {
+ uiEventLogger.log(BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS)
+ } else if (authResult == AuthenticationResult.FAILED) {
+ uiEventLogger.log(
+ BouncerUiEvent.BOUNCER_PASSWORD_FAILURE,
+ sessionTracker.getSessionId(SESSION_KEYGUARD)
+ )
+ }
+ }
+
return authResult
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
index e789475..62ef365 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
@@ -18,7 +18,7 @@
import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import dagger.Module
import dagger.Provides
@@ -42,11 +42,10 @@
fun isOnlyComposeBouncerEnabled(): Boolean
}
-class ComposeBouncerFlagsImpl(private val sceneContainerFlags: SceneContainerFlags) :
- ComposeBouncerFlags {
+class ComposeBouncerFlagsImpl() : ComposeBouncerFlags {
override fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
- return sceneContainerFlags.isEnabled() || Flags.composeBouncer()
+ return SceneContainerFlag.isEnabled || Flags.composeBouncer()
}
@Deprecated(
@@ -55,7 +54,7 @@
replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
)
override fun isOnlyComposeBouncerEnabled(): Boolean {
- return !sceneContainerFlags.isEnabled() && Flags.composeBouncer()
+ return !SceneContainerFlag.isEnabled && Flags.composeBouncer()
}
}
@@ -63,7 +62,7 @@
object ComposeBouncerFlagsModule {
@Provides
@SysUISingleton
- fun impl(sceneContainerFlags: SceneContainerFlags): ComposeBouncerFlags {
- return ComposeBouncerFlagsImpl(sceneContainerFlags)
+ fun impl(): ComposeBouncerFlags {
+ return ComposeBouncerFlagsImpl()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/logging/BouncerUiEvent.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/logging/BouncerUiEvent.kt
new file mode 100644
index 0000000..3be5499
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/logging/BouncerUiEvent.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.shared.logging
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+/**
+ * Legacy bouncer UI events {@link com.android.keyguard.KeyguardSecurityContainer.BouncerUiEvent}.
+ * Only contains that used by metrics.
+ */
+enum class BouncerUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Bouncer is dismissed using extended security access.")
+ BOUNCER_DISMISS_EXTENDED_ACCESS(413),
+
+ // PASSWORD here includes password, pattern, and pin.
+ @UiEvent(doc = "Bouncer is successfully unlocked using password.")
+ BOUNCER_PASSWORD_SUCCESS(418),
+ @UiEvent(doc = "An attempt to unlock bouncer using password has failed.")
+ BOUNCER_PASSWORD_FAILURE(419);
+
+ override fun getId() = _id
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 5c07cc5..7c41b75 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -21,6 +21,12 @@
import android.content.Context
import android.graphics.Bitmap
import androidx.core.graphics.drawable.toBitmap
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
@@ -35,6 +41,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.inputmethod.domain.interactor.InputMethodInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.user.ui.viewmodel.UserActionViewModel
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
@@ -82,6 +89,15 @@
initialValue = null,
)
+ val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
+ bouncerInteractor.dismissDestination
+ .map(::destinationSceneMap)
+ .stateIn(
+ applicationScope,
+ SharingStarted.WhileSubscribed(),
+ initialValue = destinationSceneMap(Scenes.Lockscreen),
+ )
+
val message: BouncerMessageViewModel = bouncerMessageViewModel
val userSwitcherDropdown: StateFlow<List<UserSwitcherDropdownItemViewModel>> =
@@ -310,8 +326,7 @@
{ message },
failedAttempts,
remainingAttempts,
- )
- ?: message
+ ) ?: message
} else {
message
}
@@ -328,8 +343,7 @@
.KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE,
{ message },
failedAttempts,
- )
- ?: message
+ ) ?: message
} else {
message
}
@@ -357,6 +371,12 @@
}
}
+ private fun destinationSceneMap(prevScene: SceneKey) =
+ mapOf(
+ Back to UserActionResult(prevScene),
+ Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
+ )
+
data class DialogViewModel(
val text: String,
@@ -400,13 +420,13 @@
simBouncerInteractor = simBouncerInteractor,
authenticationInteractor = authenticationInteractor,
selectedUserInteractor = selectedUserInteractor,
+ devicePolicyManager = devicePolicyManager,
+ bouncerMessageViewModel = bouncerMessageViewModel,
flags = flags,
selectedUser = userSwitcherViewModel.selectedUser,
users = userSwitcherViewModel.users,
userSwitcherMenu = userSwitcherViewModel.menu,
actionButton = actionButtonInteractor.actionButton,
- devicePolicyManager = devicePolicyManager,
- bouncerMessageViewModel = bouncerMessageViewModel,
)
}
}
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 12cac92..4c2380c 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
@@ -135,8 +135,11 @@
onIntentionalUserInput()
- mutablePinInput.value = pinInput.append(input)
- tryAuthenticate(useAutoConfirm = true)
+ val maxInputLength = hintedPinLength.value ?: Int.MAX_VALUE
+ if (pinInput.getPin().size < maxInputLength) {
+ mutablePinInput.value = pinInput.append(input)
+ tryAuthenticate(useAutoConfirm = true)
+ }
}
/** Notifies that the user clicked the backspace button. */
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
index af467ef..613280c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
@@ -22,7 +22,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.statusbar.phone.NotificationTapHelper;
import dagger.Binds;
@@ -51,9 +51,8 @@
@SysUISingleton
static FalsingCollector providesFalsingCollectorLegacy(
FalsingCollectorImpl impl,
- FalsingCollectorNoOp noOp,
- SceneContainerFlags flags) {
- return flags.isEnabled() ? noOp : impl;
+ FalsingCollectorNoOp noOp) {
+ return SceneContainerFlag.isEnabled() ? noOp : impl;
}
/** Provides the actual {@link FalsingCollector}. */
diff --git a/packages/SystemUI/src/com/android/systemui/common/coroutine/ConflatedCallbackFlow.kt b/packages/SystemUI/src/com/android/systemui/common/coroutine/ConflatedCallbackFlow.kt
index d4a1f74..0c181e9 100644
--- a/packages/SystemUI/src/com/android/systemui/common/coroutine/ConflatedCallbackFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/coroutine/ConflatedCallbackFlow.kt
@@ -16,14 +16,14 @@
package com.android.systemui.common.coroutine
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow as wrapped
import kotlin.experimental.ExperimentalTypeInference
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.callbackFlow
+@Deprecated("Use com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow instead")
object ConflatedCallbackFlow {
/**
@@ -32,9 +32,15 @@
* consumer(s) of the values in the flow), the values are buffered and, if the buffer fills up,
* we drop the oldest values automatically instead of suspending the producer.
*/
- @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
- @OptIn(ExperimentalTypeInference::class, ExperimentalCoroutinesApi::class)
+ @Deprecated(
+ "Use com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow instead",
+ ReplaceWith(
+ "conflatedCallbackFlow",
+ "com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow"
+ )
+ )
+ @OptIn(ExperimentalTypeInference::class)
fun <T> conflatedCallbackFlow(
@BuilderInference block: suspend ProducerScope<T>.() -> Unit,
- ): Flow<T> = callbackFlow(block).buffer(capacity = Channel.CONFLATED)
+ ): Flow<T> = wrapped(block)
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
index 5f6ff82..638af58 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
@@ -56,6 +56,13 @@
val naturalMaxBounds: Flow<Rect> =
repository.configurationValues.map { it.naturalScreenBounds }.distinctUntilChanged()
+ /**
+ * The layout direction. Will be either `View#LAYOUT_DIRECTION_LTR` or
+ * `View#LAYOUT_DIRECTION_RTL`.
+ */
+ val layoutDirection: Flow<Int> =
+ repository.configurationValues.map { it.layoutDirection }.distinctUntilChanged()
+
/** Given [resourceId], emit the dimension pixel size on config change */
fun dimensionPixelSize(resourceId: Int): Flow<Int> {
return onAnyConfigurationChange.mapLatest { repository.getDimensionPixelSize(resourceId) }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 4d328d6..5a174b9 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -21,6 +21,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -70,9 +71,9 @@
keyguardTransitionInteractor.startedKeyguardTransitionStep
.mapLatest(::determineSceneAfterTransition)
.filterNotNull()
- // TODO(b/322787129): Also set a custom transition animation here to avoid the regular
- // slide-in animation when setting the scene programmatically
- .onEach { nextScene -> communalInteractor.changeScene(nextScene) }
+ .onEach { nextScene ->
+ communalInteractor.changeScene(nextScene, CommunalTransitionKeys.SimpleFade)
+ }
.launchIn(applicationScope)
// TODO(b/322787129): re-enable once custom animations are in place
@@ -143,7 +144,14 @@
val docked = dockManager.isDocked
return when {
- docked && to == KeyguardState.LOCKSCREEN && from == KeyguardState.DREAMING -> {
+ to == KeyguardState.OCCLUDED -> {
+ // Hide communal when an activity is started on keyguard, to ensure the activity
+ // underneath the hub is shown.
+ CommunalScenes.Blank
+ }
+ to == KeyguardState.GLANCEABLE_HUB && from == KeyguardState.OCCLUDED -> {
+ // When transitioning to the hub from an occluded state, fade out the hub without
+ // doing any translation.
CommunalScenes.Communal
}
to == KeyguardState.GONE -> CommunalScenes.Blank
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index 72dcb26..27af99e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -24,6 +24,8 @@
import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.util.CommunalColors
+import com.android.systemui.communal.util.CommunalColorsImpl
import com.android.systemui.communal.widgets.CommunalWidgetModule
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl
@@ -60,6 +62,8 @@
@Communal
fun bindCommunalSceneDataSource(@Communal delegator: SceneDataSourceDelegator): SceneDataSource
+ @Binds fun bindCommunalColors(impl: CommunalColorsImpl): CommunalColors
+
companion object {
@Provides
@Communal
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index c724244..9debe0e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -57,6 +57,9 @@
* Settings.
*/
fun getWidgetCategories(user: UserInfo): Flow<CommunalWidgetCategories>
+
+ /** Keyguard widgets enabled state by Device Policy Manager for the specified user. */
+ fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean>
}
@SysUISingleton
@@ -115,6 +118,16 @@
}
.flowOn(bgDispatcher)
+ override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
+ broadcastDispatcher
+ .broadcastFlow(
+ filter =
+ IntentFilter(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ user = user.userHandle
+ )
+ .emitOnStart()
+ .map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) }
+
private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
secureSettings
.observerFlow(userId = user.id, names = arrayOf(Settings.Secure.GLANCEABLE_HUB_ENABLED))
@@ -128,16 +141,6 @@
) == 1
}
- private fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
- broadcastDispatcher
- .broadcastFlow(
- filter =
- IntentFilter(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- user = user.userHandle
- )
- .emitOnStart()
- .map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) }
-
companion object {
const val GLANCEABLE_HUB_CONTENT_SETTING = "glanceable_hub_content_setting"
private const val ENABLED_SETTING_DEFAULT = 1
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 246d5d9..619e052 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -44,6 +44,8 @@
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dock.DockManager
+import com.android.systemui.dock.retrieveIsDocked
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
@@ -53,7 +55,7 @@
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.UserTracker
import com.android.systemui.smartspace.data.repository.SmartspaceRepository
@@ -97,14 +99,14 @@
mediaRepository: CommunalMediaRepository,
smartspaceRepository: SmartspaceRepository,
keyguardInteractor: KeyguardInteractor,
- communalSettingsInteractor: CommunalSettingsInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
private val appWidgetHost: CommunalAppWidgetHost,
private val editWidgetsActivityStarter: EditWidgetsActivityStarter,
private val userTracker: UserTracker,
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
+ private val dockManager: DockManager,
sceneInteractor: SceneInteractor,
- sceneContainerFlags: SceneContainerFlags,
@CommunalLog logBuffer: LogBuffer,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
) {
@@ -123,7 +125,7 @@
and(
communalSettingsInteractor.isCommunalEnabled,
not(keyguardInteractor.isEncryptedOrLockdown),
- or(keyguardInteractor.isKeyguardVisible, keyguardInteractor.isDreaming)
+ or(keyguardInteractor.isKeyguardShowing, keyguardInteractor.isDreaming)
)
.distinctUntilChanged()
.onEach { available ->
@@ -143,6 +145,9 @@
replay = 1,
)
+ /** Whether to show communal by default */
+ val showByDefault: Flow<Boolean> = and(isCommunalAvailable, dockManager.retrieveIsDocked())
+
/**
* Target scene as requested by the underlying [SceneTransitionLayout] or through [changeScene].
*
@@ -210,7 +215,7 @@
*/
// TODO(b/323215860): rename to something more appropriate after cleaning up usages
val isCommunalShowing: Flow<Boolean> =
- flow { emit(sceneContainerFlags.isEnabled()) }
+ flow { emit(SceneContainerFlag.isEnabled) }
.flatMapLatest { sceneContainerEnabled ->
if (sceneContainerEnabled) {
sceneInteractor.currentScene.map { it == Scenes.Communal }
@@ -352,7 +357,14 @@
/** A list of widget content to be displayed in the communal hub. */
val widgetContent: Flow<List<WidgetContent>> =
combine(
- widgetRepository.communalWidgets.map { filterWidgetsByExistingUsers(it) },
+ widgetRepository.communalWidgets
+ .map { filterWidgetsByExistingUsers(it) }
+ .combine(communalSettingsInteractor.allowedByDevicePolicyForWorkProfile) {
+ // exclude widgets under work profile if not allowed by device policy
+ widgets,
+ allowedForWorkProfile ->
+ filterWidgetsAllowedByDevicePolicy(widgets, allowedForWorkProfile)
+ },
communalSettingsInteractor.communalWidgetCategories,
updateOnWorkProfileBroadcastReceived,
) { widgets, allowedCategories, _ ->
@@ -374,6 +386,19 @@
}
}
+ /** Filter widgets based on whether their associated profile is allowed by device policy. */
+ private fun filterWidgetsAllowedByDevicePolicy(
+ list: List<CommunalWidgetContentModel>,
+ allowedByDevicePolicyForWorkProfile: Boolean
+ ): List<CommunalWidgetContentModel> =
+ if (allowedByDevicePolicyForWorkProfile) {
+ list
+ } else {
+ // Get associated work profile for the currently selected user.
+ val workProfile = userTracker.userProfiles.find { it.isManagedProfile }
+ list.filter { it.providerInfo.profile.identifier != workProfile?.id }
+ }
+
/** A flow of available smartspace targets. Currently only showing timers. */
private val smartspaceTargets: Flow<List<SmartspaceTarget>> =
if (!smartspaceRepository.isSmartspaceRemoteViewsEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index 20f60b7..f9de609 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -16,6 +16,8 @@
package com.android.systemui.communal.domain.interactor
+import android.content.pm.UserInfo
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.communal.data.model.CommunalEnabledState
import com.android.systemui.communal.data.model.CommunalWidgetCategories
import com.android.systemui.communal.data.repository.CommunalSettingsRepository
@@ -24,13 +26,18 @@
import com.android.systemui.log.dagger.CommunalTableLog
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.settings.UserTracker
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -40,8 +47,10 @@
@Inject
constructor(
@Background private val bgScope: CoroutineScope,
+ @Background private val bgExecutor: Executor,
private val repository: CommunalSettingsRepository,
userInteractor: SelectedUserInteractor,
+ private val userTracker: UserTracker,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
) {
/** Whether or not communal is enabled for the currently selected user. */
@@ -68,4 +77,33 @@
started = SharingStarted.Eagerly,
initialValue = CommunalWidgetCategories().categories
)
+
+ private val workProfileUserInfoCallbackFlow: Flow<UserInfo?> = conflatedCallbackFlow {
+ fun send(profiles: List<UserInfo>) {
+ trySend(profiles.find { it.isManagedProfile })
+ }
+
+ val callback =
+ object : UserTracker.Callback {
+ override fun onProfilesChanged(profiles: List<UserInfo>) {
+ send(profiles)
+ }
+ }
+ userTracker.addCallback(callback, bgExecutor)
+ send(userTracker.userProfiles)
+
+ awaitClose { userTracker.removeCallback(callback) }
+ }
+
+ /** Whether or not keyguard widgets are allowed for work profile by device policy manager. */
+ val allowedByDevicePolicyForWorkProfile: StateFlow<Boolean> =
+ workProfileUserInfoCallbackFlow
+ .flatMapLatest { workProfile ->
+ workProfile?.let { repository.getAllowedByDevicePolicy(it) } ?: flowOf(false)
+ }
+ .stateIn(
+ scope = bgScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
new file mode 100644
index 0000000..a3c61a4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.shared.model
+
+import com.android.compose.animation.scene.TransitionKey
+
+/**
+ * Defines all known named transitions for [CommunalScenes].
+ *
+ * These transitions can be referenced by key when changing scenes programmatically.
+ */
+object CommunalTransitionKeys {
+ /** Fades the glanceable hub without any translation */
+ val SimpleFade = TransitionKey("SimpleFade")
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 095222a..71d719d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -36,6 +36,9 @@
) {
val currentScene: Flow<SceneKey> = communalInteractor.desiredScene
+ /** Whether communal hub can be focused to enable accessibility actions. */
+ val isFocusable: Flow<Boolean> = communalInteractor.isIdleOnCommunal
+
/** Whether widgets are currently being re-ordered. */
open val reorderingWidgets: StateFlow<Boolean> = MutableStateFlow(false)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
index 96e4b34..1bee83b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
@@ -16,7 +16,13 @@
package com.android.systemui.communal.ui.viewmodel
+import android.graphics.Color
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel
@@ -24,7 +30,9 @@
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.merge
/** View model for transitions related to the communal hub. */
@@ -33,10 +41,13 @@
class CommunalTransitionViewModel
@Inject
constructor(
+ communalColors: CommunalColors,
glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel,
lockscreenToGlanceableHubTransitionViewModel: LockscreenToGlanceableHubTransitionViewModel,
dreamToGlanceableHubTransitionViewModel: DreamingToGlanceableHubTransitionViewModel,
glanceableHubToDreamTransitionViewModel: GlanceableHubToDreamingTransitionViewModel,
+ communalInteractor: CommunalInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
/**
* Whether UMO location should be on communal. This flow is responsive to transitions so that a
@@ -51,4 +62,23 @@
glanceableHubToDreamTransitionViewModel.showUmo,
)
.distinctUntilChanged()
+
+ /** Whether to show communal by default */
+ val showByDefault: Flow<Boolean> = communalInteractor.showByDefault
+
+ val transitionFromOccludedEnded =
+ keyguardTransitionInteractor.transitionStepsFromState(KeyguardState.OCCLUDED).filter { step
+ ->
+ step.transitionState == TransitionState.FINISHED ||
+ step.transitionState == TransitionState.CANCELED
+ }
+
+ val recentsBackgroundColor: Flow<Color?> =
+ combine(showByDefault, communalColors.backgroundColor) { showByDefault, backgroundColor ->
+ if (showByDefault) {
+ backgroundColor
+ } else {
+ null
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/CommunalColors.kt b/packages/SystemUI/src/com/android/systemui/communal/util/CommunalColors.kt
new file mode 100644
index 0000000..1e04fe7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/CommunalColors.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.util
+
+import android.content.Context
+import android.graphics.Color
+import com.android.settingslib.Utils
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/** Wrapper around colors used for the communal UI. */
+interface CommunalColors {
+ /** The background color of the glanceable hub. */
+ val backgroundColor: StateFlow<Color>
+}
+
+@SysUISingleton
+class CommunalColorsImpl
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ private val context: Context,
+ configurationInteractor: ConfigurationInteractor,
+) : CommunalColors {
+ override val backgroundColor: StateFlow<Color> =
+ configurationInteractor.onAnyConfigurationChange
+ .map { loadBackgroundColor() }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = loadBackgroundColor()
+ )
+
+ private fun loadBackgroundColor(): Color =
+ Color.valueOf(
+ Utils.getColorAttrDefaultColor(
+ context,
+ com.android.internal.R.attr.materialColorOutlineVariant
+ )
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7d86e06..6b85d30 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -32,6 +32,7 @@
import com.android.systemui.SystemUISecondaryUserService;
import com.android.systemui.accessibility.AccessibilityModule;
import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
+import com.android.systemui.ambient.dagger.AmbientModule;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.authentication.AuthenticationModule;
@@ -162,14 +163,14 @@
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
+import kotlinx.coroutines.CoroutineScope;
+
import java.util.Collections;
import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Named;
-import kotlinx.coroutines.CoroutineScope;
-
/**
* A dagger module for injecting components of System UI that are required by System UI.
*
@@ -183,6 +184,7 @@
@Module(includes = {
AccessibilityModule.class,
AccessibilityRepositoryModule.class,
+ AmbientModule.class,
AppOpsModule.class,
AssistModule.class,
AuthenticationModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index 0e04d15..e418641 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -50,6 +50,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
@@ -294,7 +295,8 @@
}
private fun listenForSchedulingWatchdog() {
- keyguardTransitionInteractor.anyStateToGoneTransition
+ keyguardTransitionInteractor
+ .transition(to = KeyguardState.GONE)
.filter { it.transitionState == TransitionState.FINISHED }
.onEach {
// We deliberately want to run this in background because scheduleWatchdog does
@@ -312,7 +314,17 @@
// or device starts going to sleep.
merge(
powerInteractor.isAsleep,
- keyguardTransitionInteractor.isInTransitionToState(KeyguardState.GONE),
+ combine(
+ keyguardTransitionInteractor.isFinishedInState(KeyguardState.GONE),
+ keyguardInteractor.statusBarState,
+ ) { isFinishedInGoneState, statusBarState ->
+ // When the user is dragging the primary bouncer in (up) by manually scrolling
+ // up on the lockscreen, the device won't be irreversibly transitioned to GONE
+ // until the statusBarState updates to SHADE, so we check that here.
+ // Else, we could reset the face auth state too early and end up in a strange
+ // state.
+ isFinishedInGoneState && statusBarState == StatusBarState.SHADE
+ },
userRepository.selectedUser.map {
it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
},
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index a7266503..03819ed 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -37,6 +37,10 @@
import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -121,9 +125,9 @@
.launchIn(applicationScope)
merge(
- keyguardTransitionInteractor.aodToLockscreenTransition,
- keyguardTransitionInteractor.offToLockscreenTransition,
- keyguardTransitionInteractor.dozingToLockscreenTransition
+ keyguardTransitionInteractor.transition(AOD, LOCKSCREEN),
+ keyguardTransitionInteractor.transition(OFF, LOCKSCREEN),
+ keyguardTransitionInteractor.transition(DOZING, LOCKSCREEN),
)
.filter { it.transitionState == TransitionState.STARTED }
.sample(powerInteractor.detailedWakefulness)
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
deleted file mode 100644
index 989b0de..0000000
--- a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.display.ui.view
-
-import android.content.Context
-import android.os.Bundle
-import android.view.View
-import android.view.WindowInsets
-import android.widget.TextView
-import androidx.core.view.updatePadding
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.phone.SystemUIBottomSheetDialog
-import com.android.systemui.statusbar.policy.ConfigurationController
-import kotlin.math.max
-
-/**
- * Dialog used to decide what to do with a connected display.
- *
- * [onCancelMirroring] is called **only** if mirroring didn't start, or when the dismiss button is
- * pressed.
- */
-class MirroringConfirmationDialog(
- context: Context,
- private val onStartMirroringClickListener: View.OnClickListener,
- private val onCancelMirroring: View.OnClickListener,
- private val navbarBottomInsetsProvider: () -> Int,
- configurationController: ConfigurationController? = null,
- private val showConcurrentDisplayInfo: Boolean = false,
- theme: Int = R.style.Theme_SystemUI_Dialog,
-) : SystemUIBottomSheetDialog(context, configurationController, theme) {
-
- private lateinit var mirrorButton: TextView
- private lateinit var dismissButton: TextView
- private lateinit var dualDisplayWarning: TextView
- private lateinit var bottomSheet: View
- private var enabledPressed = false
- private val defaultDialogBottomInset =
- context.resources.getDimensionPixelSize(R.dimen.dialog_bottom_padding)
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.connected_display_dialog)
-
- mirrorButton =
- requireViewById<TextView>(R.id.enable_display).apply {
- setOnClickListener(onStartMirroringClickListener)
- enabledPressed = true
- }
- dismissButton =
- requireViewById<TextView>(R.id.cancel).apply { setOnClickListener(onCancelMirroring) }
-
- dualDisplayWarning =
- requireViewById<TextView>(R.id.dual_display_warning).apply {
- visibility = if (showConcurrentDisplayInfo) View.VISIBLE else View.GONE
- }
-
- bottomSheet = requireViewById(R.id.cd_bottom_sheet)
-
- setOnDismissListener {
- if (!enabledPressed) {
- onCancelMirroring.onClick(null)
- }
- }
- setupInsets()
- }
-
- private fun setupInsets(navbarInsets: Int = navbarBottomInsetsProvider()) {
- // This avoids overlap between dialog content and navigation bars.
- // we only care about the bottom inset as in all other configuration where navigations
- // are in other display sides there is no overlap with the dialog.
- bottomSheet.updatePadding(bottom = max(navbarInsets, defaultDialogBottomInset))
- }
-
- override fun onInsetsChanged(changedTypes: Int, insets: WindowInsets) {
- val navbarType = WindowInsets.Type.navigationBars()
- if (changedTypes and navbarType != 0) {
- setupInsets(insets.getInsets(navbarType).bottom)
- }
- }
-
- override fun onConfigurationChanged() {
- super.onConfigurationChanged()
- setupInsets()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegate.kt
new file mode 100644
index 0000000..19b2673
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegate.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.display.ui.view
+
+import android.app.Dialog
+import android.content.Context
+import android.content.res.Configuration
+import android.os.Bundle
+import android.view.View
+import android.view.WindowInsets
+import android.view.WindowInsetsAnimation
+import android.widget.TextView
+import androidx.annotation.StyleRes
+import androidx.annotation.VisibleForTesting
+import androidx.core.view.updatePadding
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.DialogDelegate
+import com.android.systemui.statusbar.phone.SystemUIBottomSheetDialog
+import javax.inject.Inject
+import kotlin.math.max
+
+/**
+ * Dialog used to decide what to do with a connected display.
+ *
+ * [onCancelMirroring] is called **only** if mirroring didn't start, or when the dismiss button is
+ * pressed.
+ */
+class MirroringConfirmationDialogDelegate
+@VisibleForTesting
+constructor(
+ context: Context,
+ private val showConcurrentDisplayInfo: Boolean = false,
+ private val onStartMirroringClickListener: View.OnClickListener,
+ private val onCancelMirroring: View.OnClickListener,
+ private val navbarBottomInsetsProvider: () -> Int,
+) : DialogDelegate<Dialog> {
+
+ private lateinit var mirrorButton: TextView
+ private lateinit var dismissButton: TextView
+ private lateinit var dualDisplayWarning: TextView
+ private lateinit var bottomSheet: View
+ private var enabledPressed = false
+ private val defaultDialogBottomInset =
+ context.resources.getDimensionPixelSize(R.dimen.dialog_bottom_padding)
+
+ override fun onCreate(dialog: Dialog, savedInstanceState: Bundle?) {
+ dialog.setContentView(R.layout.connected_display_dialog)
+
+ mirrorButton =
+ dialog.requireViewById<TextView>(R.id.enable_display).apply {
+ setOnClickListener(onStartMirroringClickListener)
+ enabledPressed = true
+ }
+ dismissButton =
+ dialog.requireViewById<TextView>(R.id.cancel).apply {
+ setOnClickListener(onCancelMirroring)
+ }
+
+ dualDisplayWarning =
+ dialog.requireViewById<TextView>(R.id.dual_display_warning).apply {
+ visibility = if (showConcurrentDisplayInfo) View.VISIBLE else View.GONE
+ }
+
+ bottomSheet = dialog.requireViewById(R.id.cd_bottom_sheet)
+
+ dialog.setOnDismissListener {
+ if (!enabledPressed) {
+ onCancelMirroring.onClick(null)
+ }
+ }
+ setupInsets()
+ }
+
+ override fun onStart(dialog: Dialog) {
+ dialog.window?.decorView?.setWindowInsetsAnimationCallback(insetsAnimationCallback)
+ }
+
+ override fun onStop(dialog: Dialog) {
+ dialog.window?.decorView?.setWindowInsetsAnimationCallback(null)
+ }
+
+ private fun setupInsets(navbarInsets: Int = navbarBottomInsetsProvider()) {
+ // This avoids overlap between dialog content and navigation bars.
+ // we only care about the bottom inset as in all other configuration where navigations
+ // are in other display sides there is no overlap with the dialog.
+ bottomSheet.updatePadding(bottom = max(navbarInsets, defaultDialogBottomInset))
+ }
+
+ override fun onConfigurationChanged(dialog: Dialog, configuration: Configuration) {
+ setupInsets()
+ }
+
+ private val insetsAnimationCallback =
+ object : WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+
+ private var lastInsets: WindowInsets? = null
+
+ override fun onEnd(animation: WindowInsetsAnimation) {
+ lastInsets?.let { onInsetsChanged(animation.typeMask, it) }
+ }
+
+ override fun onProgress(
+ insets: WindowInsets,
+ animations: MutableList<WindowInsetsAnimation>,
+ ): WindowInsets {
+ lastInsets = insets
+ onInsetsChanged(changedTypes = allAnimationMasks(animations), insets)
+ return insets
+ }
+
+ private fun allAnimationMasks(animations: List<WindowInsetsAnimation>): Int =
+ animations.fold(0) { acc: Int, it -> acc or it.typeMask }
+
+ private fun onInsetsChanged(changedTypes: Int, insets: WindowInsets) {
+ val navbarType = WindowInsets.Type.navigationBars()
+ if (changedTypes and navbarType != 0) {
+ setupInsets(insets.getInsets(navbarType).bottom)
+ }
+ }
+ }
+
+ class Factory
+ @Inject
+ constructor(
+ @Application private val context: Context,
+ private val dialogFactory: SystemUIBottomSheetDialog.Factory,
+ ) {
+
+ fun createDialog(
+ showConcurrentDisplayInfo: Boolean = false,
+ onStartMirroringClickListener: View.OnClickListener,
+ onCancelMirroring: View.OnClickListener,
+ navbarBottomInsetsProvider: () -> Int,
+ @StyleRes theme: Int = R.style.Theme_SystemUI_Dialog,
+ ): Dialog =
+ dialogFactory.create(
+ delegate =
+ MirroringConfirmationDialogDelegate(
+ context = context,
+ showConcurrentDisplayInfo = showConcurrentDisplayInfo,
+ onStartMirroringClickListener = onStartMirroringClickListener,
+ onCancelMirroring = onCancelMirroring,
+ navbarBottomInsetsProvider = navbarBottomInsetsProvider,
+ ),
+ theme = theme,
+ )
+ }
+}
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
index fbf0538..81ea2e7 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
@@ -25,8 +25,7 @@
import com.android.systemui.dagger.qualifiers.Background
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 com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.display.ui.view.MirroringConfirmationDialogDelegate
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -54,7 +53,7 @@
private val connectedDisplayInteractor: ConnectedDisplayInteractor,
@Application private val scope: CoroutineScope,
@Background private val bgDispatcher: CoroutineDispatcher,
- private val configurationController: ConfigurationController,
+ private val bottomSheetFactory: MirroringConfirmationDialogDelegate.Factory,
) : CoreStartable {
private var dialog: Dialog? = null
@@ -91,8 +90,8 @@
private fun showDialog(pendingDisplay: PendingDisplay, concurrentDisplaysInProgess: Boolean) {
hideDialog()
dialog =
- MirroringConfirmationDialog(
- context,
+ bottomSheetFactory
+ .createDialog(
onStartMirroringClickListener = {
scope.launch(bgDispatcher) { pendingDisplay.enable() }
hideDialog()
@@ -102,7 +101,6 @@
hideDialog()
},
navbarBottomInsetsProvider = { Utils.getNavbarInsets(context).bottom },
- configurationController,
showConcurrentDisplayInfo = concurrentDisplaysInProgess
)
.apply { show() }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 1a855d7..95012a2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -54,8 +54,6 @@
private final DozeParameters mDozeParameters;
private final DozeLog mDozeLog;
private final DelayableExecutor mBgExecutor;
-
- private Runnable mCancelRunnable = null;
private long mLastTimeTickElapsed = 0;
// If time tick is scheduled and there's not a pending runnable to cancel:
private volatile boolean mTimeTickScheduled;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index d0f2559..5a036b1 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -33,14 +33,14 @@
import com.android.app.animation.Interpolators;
import com.android.dream.lowlight.LowLightTransitionCoordinator;
-import com.android.systemui.res.R;
+import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.complication.ComplicationHostViewController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.dagger.DreamOverlayModule;
-import com.android.systemui.dreams.touch.scrim.BouncerlessScrimController;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
+import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.util.ViewController;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 675e8de..1135afe 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -43,11 +43,12 @@
import com.android.internal.policy.PhoneWindow;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.ambient.touch.TouchMonitor;
+import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent;
import com.android.systemui.complication.Complication;
import com.android.systemui.complication.dagger.ComplicationComponent;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
-import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -94,6 +95,8 @@
private final ComplicationComponent mComplicationComponent;
+ private final AmbientTouchComponent mAmbientTouchComponent;
+
private final com.android.systemui.dreams.complication.dagger.ComplicationComponent
mDreamComplicationComponent;
@@ -102,7 +105,7 @@
private final DreamOverlayLifecycleOwner mLifecycleOwner;
private final LifecycleRegistry mLifecycleRegistry;
- private DreamOverlayTouchMonitor mDreamOverlayTouchMonitor;
+ private TouchMonitor mTouchMonitor;
private final KeyguardUpdateMonitorCallback mKeyguardCallback =
new KeyguardUpdateMonitorCallback() {
@@ -162,6 +165,7 @@
com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory
dreamComplicationComponentFactory,
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
+ AmbientTouchComponent.Factory ambientTouchComponentFactory,
DreamOverlayStateController stateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
UiEventLogger uiEventLogger,
@@ -194,9 +198,11 @@
mDreamComplicationComponent = dreamComplicationComponentFactory.create(
mComplicationComponent.getVisibilityController(), touchInsetManager);
mDreamOverlayComponent = dreamOverlayComponentFactory.create(lifecycleOwner,
- mComplicationComponent.getComplicationHostViewController(), touchInsetManager,
+ mComplicationComponent.getComplicationHostViewController(), touchInsetManager);
+ mAmbientTouchComponent = ambientTouchComponentFactory.create(lifecycleOwner,
new HashSet<>(Arrays.asList(
- mDreamComplicationComponent.getHideComplicationTouchHandler())));
+ mDreamComplicationComponent.getHideComplicationTouchHandler(),
+ mDreamOverlayComponent.getCommunalTouchHandler())));
mLifecycleOwner = lifecycleOwner;
mLifecycleRegistry = mLifecycleOwner.getRegistry();
@@ -239,8 +245,8 @@
mDreamOverlayContainerViewController =
mDreamOverlayComponent.getDreamOverlayContainerViewController();
- mDreamOverlayTouchMonitor = mDreamOverlayComponent.getDreamOverlayTouchMonitor();
- mDreamOverlayTouchMonitor.init();
+ mTouchMonitor = mAmbientTouchComponent.getTouchMonitor();
+ mTouchMonitor.init();
mStateController.setShouldShowComplications(shouldShowComplications());
@@ -370,7 +376,7 @@
mStateController.setEntryAnimationsFinished(false);
mDreamOverlayContainerViewController = null;
- mDreamOverlayTouchMonitor = null;
+ mTouchMonitor = null;
mWindow = null;
mStarted = false;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 8d8702e..da72a56 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -16,30 +16,30 @@
package com.android.systemui.dreams;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+
import android.app.AlarmManager;
import android.app.StatusBarManager;
import android.content.res.Resources;
import android.hardware.SensorPrivacyManager;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.PluralsMessageFormatter;
import android.view.View;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.dagger.DreamLog;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CrossFadeHelper;
+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.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -65,7 +65,6 @@
public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> {
private static final String TAG = "DreamStatusBarCtrl";
- private final ConnectivityManager mConnectivityManager;
private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
private final NextAlarmController mNextAlarmController;
private final AlarmManager mAlarmManager;
@@ -77,6 +76,7 @@
private final ZenModeController mZenModeController;
private final DreamOverlayStateController mDreamOverlayStateController;
private final UserTracker mUserTracker;
+ private final WifiRepository mWifiRepository;
private final StatusBarWindowStateController mStatusBarWindowStateController;
private final DreamOverlayStatusBarItemsProvider mStatusBarItemsProvider;
private final Executor mMainExecutor;
@@ -89,28 +89,6 @@
// Whether dream entry animations are finished.
private boolean mEntryAnimationsFinished = false;
- private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
- .clearCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
-
- private final NetworkCallback mNetworkCallback = new NetworkCallback() {
- @Override
- public void onCapabilitiesChanged(
- Network network, NetworkCapabilities networkCapabilities) {
- updateWifiUnavailableStatusIcon();
- }
-
- @Override
- public void onAvailable(Network network) {
- updateWifiUnavailableStatusIcon();
- }
-
- @Override
- public void onLost(Network network) {
- updateWifiUnavailableStatusIcon();
- }
- };
-
private final DreamOverlayStateController.Callback mDreamOverlayStateCallback =
new DreamOverlayStateController.Callback() {
@Override
@@ -151,7 +129,6 @@
DreamOverlayStatusBarView view,
@Main Resources resources,
@Main Executor mainExecutor,
- ConnectivityManager connectivityManager,
TouchInsetManager.TouchInsetSession touchInsetSession,
AlarmManager alarmManager,
NextAlarmController nextAlarmController,
@@ -163,11 +140,11 @@
DreamOverlayStatusBarItemsProvider statusBarItemsProvider,
DreamOverlayStateController dreamOverlayStateController,
UserTracker userTracker,
+ WifiRepository wifiRepository,
@DreamLog LogBuffer logBuffer) {
super(view);
mResources = resources;
mMainExecutor = mainExecutor;
- mConnectivityManager = connectivityManager;
mTouchInsetSession = touchInsetSession;
mAlarmManager = alarmManager;
mNextAlarmController = nextAlarmController;
@@ -179,6 +156,7 @@
mZenModeController = zenModeController;
mDreamOverlayStateController = dreamOverlayStateController;
mUserTracker = userTracker;
+ mWifiRepository = wifiRepository;
mLogger = new DreamLogger(logBuffer, TAG);
// Register to receive show/hide updates for the system status bar. Our custom status bar
@@ -190,8 +168,11 @@
protected void onViewAttached() {
mIsAttached = true;
- mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
- updateWifiUnavailableStatusIcon();
+ collectFlow(
+ mView,
+ mWifiRepository.getWifiNetwork(),
+ network -> updateWifiUnavailableStatusIcon(
+ network instanceof WifiNetworkModel.Active));
mNextAlarmController.addCallback(mNextAlarmCallback);
updateAlarmStatusIcon();
@@ -215,7 +196,6 @@
mZenModeController.removeCallback(mZenModeCallback);
mSensorPrivacyController.removeCallback(mSensorCallback);
mNextAlarmController.removeCallback(mNextAlarmCallback);
- mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
mDreamOverlayNotificationCountProvider.ifPresent(
provider -> provider.removeCallback(mNotificationCountCallback));
mStatusBarItemsProvider.removeCallback(mStatusBarItemsProviderCallback);
@@ -258,12 +238,8 @@
&& !mStatusBarWindowStateController.windowIsShowing();
}
- private void updateWifiUnavailableStatusIcon() {
- final NetworkCapabilities capabilities =
- mConnectivityManager.getNetworkCapabilities(
- mConnectivityManager.getActiveNetwork());
- final boolean available = capabilities != null
- && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
+ @VisibleForTesting
+ void updateWifiUnavailableStatusIcon(boolean available) {
showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available,
R.string.wifi_unavailable_dream_overlay_content_description);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java
index ee48ee5..f8ae5c2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java
@@ -16,6 +16,7 @@
package com.android.systemui.dreams.complication;
+import static com.android.systemui.Flags.removeDreamOverlayHideOnTouch;
import static com.android.systemui.dreams.complication.dagger.ComplicationModule.COMPLICATIONS_FADE_OUT_DELAY;
import static com.android.systemui.dreams.complication.dagger.ComplicationModule.COMPLICATIONS_RESTORE_TIMEOUT;
@@ -25,11 +26,11 @@
import androidx.annotation.Nullable;
+import com.android.systemui.ambient.touch.TouchHandler;
+import com.android.systemui.ambient.touch.TouchMonitor;
import com.android.systemui.complication.Complication;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
-import com.android.systemui.dreams.touch.DreamTouchHandler;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -46,12 +47,12 @@
* {@link HideComplicationTouchHandler} is responsible for hiding the overlay complications from
* visibility whenever there is touch interactions outside the overlay. The overlay interaction
* scope includes touches to the complication plus any touch entry region for gestures as specified
- * to the {@link DreamOverlayTouchMonitor}.
+ * to the {@link TouchMonitor}.
*
- * This {@link DreamTouchHandler} is also responsible for fading in the complications at the end
- * of the {@link com.android.systemui.dreams.touch.DreamTouchHandler.TouchSession}.
+ * This {@link TouchHandler} is also responsible for fading in the complications at the end
+ * of the {@link TouchHandler.TouchSession}.
*/
-public class HideComplicationTouchHandler implements DreamTouchHandler {
+public class HideComplicationTouchHandler implements TouchHandler {
private static final String TAG = "HideComplicationHandler";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -120,7 +121,7 @@
final boolean bouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
// If other sessions are interested in this touch, do not fade out elements.
- if (session.getActiveSessionCount() > 1 || bouncerShowing
+ if (removeDreamOverlayHideOnTouch() || session.getActiveSessionCount() > 1 || bouncerShowing
|| mOverlayStateController.areExitAnimationsRunning()) {
if (DEBUG) {
Log.d(TAG, "not fading. Active session count: " + session.getActiveSessionCount()
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index ba74742..31710ac 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -25,6 +25,7 @@
import com.android.dream.lowlight.dagger.LowLightDreamModule;
import com.android.settingslib.dream.DreamBackend;
+import com.android.systemui.ambient.touch.scrim.dagger.ScrimModule;
import com.android.systemui.complication.dagger.RegisteredComplicationsModule;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -34,7 +35,6 @@
import com.android.systemui.dreams.homecontrols.DreamActivityProvider;
import com.android.systemui.dreams.homecontrols.DreamActivityProviderImpl;
import com.android.systemui.dreams.homecontrols.HomeControlsDreamService;
-import com.android.systemui.dreams.touch.scrim.dagger.ScrimModule;
import com.android.systemui.res.R;
import com.android.systemui.touch.TouchInsetManager;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
index cb587c2..16276fe 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
@@ -16,19 +16,14 @@
package com.android.systemui.dreams.dagger;
-import static com.android.systemui.dreams.dagger.DreamOverlayModule.DREAM_TOUCH_HANDLERS;
-
import static java.lang.annotation.RetentionPolicy.RUNTIME;
-import android.annotation.Nullable;
-
import androidx.lifecycle.LifecycleOwner;
import com.android.systemui.complication.ComplicationHostViewController;
import com.android.systemui.dreams.DreamOverlayContainerViewController;
-import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
-import com.android.systemui.dreams.touch.DreamTouchHandler;
-import com.android.systemui.dreams.touch.dagger.DreamTouchModule;
+import com.android.systemui.dreams.touch.CommunalTouchHandler;
+import com.android.systemui.dreams.touch.dagger.CommunalTouchModule;
import com.android.systemui.touch.TouchInsetManager;
import dagger.BindsInstance;
@@ -36,17 +31,15 @@
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
-import java.util.Set;
-import javax.inject.Named;
import javax.inject.Scope;
/**
* Dagger subcomponent for {@link DreamOverlayModule}.
*/
@Subcomponent(modules = {
- DreamTouchModule.class,
DreamOverlayModule.class,
+ CommunalTouchModule.class
})
@DreamOverlayComponent.DreamOverlayScope
public interface DreamOverlayComponent {
@@ -56,9 +49,7 @@
DreamOverlayComponent create(
@BindsInstance LifecycleOwner lifecycleOwner,
@BindsInstance ComplicationHostViewController complicationHostViewController,
- @BindsInstance TouchInsetManager touchInsetManager,
- @BindsInstance @Named(DREAM_TOUCH_HANDLERS) @Nullable
- Set<DreamTouchHandler> dreamTouchHandlers);
+ @BindsInstance TouchInsetManager touchInsetManager);
}
/** Scope annotation for singleton items within the {@link DreamOverlayComponent}. */
@@ -70,6 +61,6 @@
/** Builds a {@link DreamOverlayContainerViewController}. */
DreamOverlayContainerViewController getDreamOverlayContainerViewController();
- /** Builds a {@link DreamOverlayTouchMonitor} */
- DreamOverlayTouchMonitor getDreamOverlayTouchMonitor();
+ /** Builds communal touch handler */
+ CommunalTouchHandler getCommunalTouchHandler();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 34dd1008..999e681 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -16,7 +16,6 @@
package com.android.systemui.dreams.dagger;
-import android.annotation.Nullable;
import android.content.res.Resources;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -25,26 +24,20 @@
import androidx.lifecycle.LifecycleOwner;
import com.android.internal.util.Preconditions;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayContainerView;
import com.android.systemui.dreams.DreamOverlayStatusBarView;
-import com.android.systemui.dreams.touch.DreamTouchHandler;
+import com.android.systemui.res.R;
import com.android.systemui.touch.TouchInsetManager;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.ElementsIntoSet;
-
-import java.util.HashSet;
-import java.util.Set;
import javax.inject.Named;
/** Dagger module for {@link DreamOverlayComponent}. */
@Module
public abstract class DreamOverlayModule {
- public static final String DREAM_TOUCH_HANDLERS = "dream_touch_handlers";
public static final String DREAM_OVERLAY_CONTENT_VIEW = "dream_overlay_content_view";
public static final String MAX_BURN_IN_OFFSET = "max_burn_in_offset";
public static final String BURN_IN_PROTECTION_UPDATE_INTERVAL =
@@ -169,11 +162,4 @@
static Lifecycle providesLifecycle(LifecycleOwner lifecycleOwner) {
return lifecycleOwner.getLifecycle();
}
-
- @Provides
- @ElementsIntoSet
- static Set<DreamTouchHandler> providesDreamTouchHandlers(
- @Named(DREAM_TOUCH_HANDLERS) @Nullable Set<DreamTouchHandler> touchHandlers) {
- return touchHandlers != null ? touchHandlers : new HashSet<>();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
index 13588c2..1c047dd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
@@ -16,7 +16,6 @@
package com.android.systemui.dreams.touch;
-import static com.android.systemui.dreams.touch.dagger.ShadeModule.COMMUNAL_GESTURE_INITIATION_WIDTH;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.graphics.Rect;
@@ -27,7 +26,9 @@
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
+import com.android.systemui.ambient.touch.TouchHandler;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.dreams.touch.dagger.CommunalTouchModule;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.util.Optional;
@@ -36,8 +37,8 @@
import javax.inject.Inject;
import javax.inject.Named;
-/** {@link DreamTouchHandler} responsible for handling touches to open communal hub. **/
-public class CommunalTouchHandler implements DreamTouchHandler {
+/** {@link TouchHandler} responsible for handling touches to open communal hub. **/
+public class CommunalTouchHandler implements TouchHandler {
private final int mInitiationWidth;
private final Optional<CentralSurfaces> mCentralSurfaces;
private final Lifecycle mLifecycle;
@@ -53,7 +54,7 @@
@Inject
public CommunalTouchHandler(
Optional<CentralSurfaces> centralSurfaces,
- @Named(COMMUNAL_GESTURE_INITIATION_WIDTH) int initiationWidth,
+ @Named(CommunalTouchModule.COMMUNAL_GESTURE_INITIATION_WIDTH) int initiationWidth,
CommunalInteractor communalInteractor,
Lifecycle lifecycle) {
mInitiationWidth = initiationWidth;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/CommunalTouchModule.kt b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/CommunalTouchModule.kt
new file mode 100644
index 0000000..927ea4e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/CommunalTouchModule.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams.touch.dagger
+
+import android.content.res.Resources
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import dagger.Module
+import dagger.Provides
+import javax.inject.Named
+
+@Module
+interface CommunalTouchModule {
+ companion object {
+ /** Provides the width of the gesture area for swiping open communal hub. */
+ @JvmStatic
+ @Provides
+ @Named(COMMUNAL_GESTURE_INITIATION_WIDTH)
+ fun providesCommunalGestureInitiationWidth(@Main resources: Resources): Int {
+ return resources.getDimensionPixelSize(R.dimen.communal_gesture_initiation_width)
+ }
+
+ /** Width of swipe gesture edge to show communal hub. */
+ const val COMMUNAL_GESTURE_INITIATION_WIDTH = "communal_gesture_initiation_width"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
deleted file mode 100644
index b719126..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
+++ /dev/null
@@ -1,33 +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.systemui.dreams.touch.dagger;
-
-import dagger.Module;
-
-/**
- * {@link DreamTouchModule} encapsulates dream touch-related components.
- */
-@Module(includes = {
- BouncerSwipeModule.class,
- ShadeModule.class,
- }, subcomponents = {
- InputSessionComponent.class,
-})
-public interface DreamTouchModule {
- String INPUT_SESSION_NAME = "INPUT_SESSION_NAME";
- String PILFER_ON_GESTURE_CONSUME = "PILFER_ON_GESTURE_CONSUME";
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
index 037c23b..2d9c14e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
@@ -21,13 +21,16 @@
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.kotlin.FlowDumperImpl
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -49,7 +52,8 @@
private val communalInteractor: CommunalInteractor,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val userTracker: UserTracker,
-) {
+ dumpManager: DumpManager,
+) : FlowDumperImpl(dumpManager) {
fun startTransitionFromDream() {
val showGlanceableHub =
@@ -83,6 +87,7 @@
toGlanceableHubTransitionViewModel.dreamAlpha,
)
.distinctUntilChanged()
+ .dumpWhileCollecting("dreamAlpha")
val dreamOverlayAlpha: Flow<Float> =
merge(
@@ -93,7 +98,7 @@
.distinctUntilChanged()
val transitionEnded =
- keyguardTransitionInteractor.fromDreamingTransition.filter { step ->
+ keyguardTransitionInteractor.transition(from = DREAMING).filter { step ->
step.transitionState == TransitionState.FINISHED ||
step.transitionState == TransitionState.CANCELED
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 640534c..db6b8fe 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -153,11 +153,6 @@
// TODO(b/267722622): Tracking Bug
@JvmField val WALLPAPER_PICKER_UI_FOR_AIWP = releasedFlag("wallpaper_picker_ui_for_aiwp")
- /** Whether to use a new data source for intents to run on keyguard dismissal. */
- // TODO(b/275069969): Tracking bug.
- @JvmField
- val REFACTOR_KEYGUARD_DISMISS_INTENT = unreleasedFlag("refactor_keyguard_dismiss_intent")
-
/** Whether to allow long-press on the lock screen to directly open wallpaper picker. */
// TODO(b/277220285): Tracking bug.
@JvmField
@@ -342,11 +337,6 @@
namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
)
- @Keep
- @JvmField
- val WM_CAPTION_ON_SHELL =
- sysPropBooleanFlag("persist.wm.debug.caption_on_shell", default = true)
-
// TODO(b/256873975): Tracking Bug
@JvmField
@Keep
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index f1620d9..4327d18 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -27,40 +27,65 @@
import androidx.core.animation.doOnCancel
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.statusbar.VibratorHelper
-import kotlinx.coroutines.CancellationException
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.launch
+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
/**
* A class that handles the long press visuo-haptic effect for a QS tile.
*
* The class is also a [View.OnTouchListener] to handle the touch events, clicks and long-press
- * gestures of the tile. The class also provides a [State] that can be used to determine the current
+ * gestures of the tile. The class also provides a [State] tha can be used to determine the current
* state of the long press effect.
*
* @property[vibratorHelper] The [VibratorHelper] to deliver haptic effects.
* @property[effectDuration] The duration of the effect in ms.
*/
-class QSLongPressEffect(
+// TODO(b/332902869): In addition from being injectable, we can consider making it a singleton
+class QSLongPressEffect
+@Inject
+constructor(
private val vibratorHelper: VibratorHelper?,
- private val effectDuration: Int,
+ val keyguardInteractor: KeyguardInteractor,
+ @Background bgScope: CoroutineScope,
) : View.OnTouchListener {
+ private var effectDuration = 0
+
/** Current state */
- var state = State.IDLE
- @VisibleForTesting set
+ private var _state = MutableStateFlow(State.IDLE)
+ val state = _state.stateIn(bgScope, SharingStarted.Lazily, State.IDLE)
/** Flows for view control and action */
private val _effectProgress = MutableStateFlow<Float?>(null)
- val effectProgress = _effectProgress.asStateFlow()
+ val effectProgress = _effectProgress.stateIn(bgScope, SharingStarted.Lazily, null)
- private val _actionType = MutableStateFlow<ActionType?>(null)
- val actionType = _actionType.asStateFlow()
+ // Actions to perform
+ private val _postedActionType = MutableStateFlow<ActionType?>(null)
+ val actionType: StateFlow<ActionType?> =
+ combine(
+ _postedActionType,
+ keyguardInteractor.isKeyguardDismissible,
+ ) { action, isDismissible ->
+ if (!isDismissible && action == ActionType.LONG_PRESS) {
+ ActionType.RESET_AND_LONG_PRESS
+ } else {
+ action
+ }
+ }
+ .stateIn(bgScope, SharingStarted.Lazily, null)
+
+ // Should a tap timeout countdown begin
+ val shouldWaitForTapTimeout: Flow<Boolean> = state.map { it == State.TIMEOUT_WAIT }
/** Haptic effects */
private val durations =
@@ -69,41 +94,33 @@
VibrationEffect.Composition.PRIMITIVE_SPIN
)
- private val longPressHint =
- LongPressHapticBuilder.createLongPressHint(
- durations?.get(0) ?: LongPressHapticBuilder.INVALID_DURATION,
- durations?.get(1) ?: LongPressHapticBuilder.INVALID_DURATION,
- effectDuration
- )
+ private var longPressHint: VibrationEffect? = null
private val snapEffect = LongPressHapticBuilder.createSnapEffect()
- /* A coroutine scope and a timer job that waits for the pressedTimeout */
- var scope: CoroutineScope? = null
- private var waitJob: Job? = null
+ private var effectAnimator: ValueAnimator? = null
- private val effectAnimator =
- ValueAnimator.ofFloat(0f, 1f).apply {
- duration = effectDuration.toLong()
- interpolator = AccelerateDecelerateInterpolator()
+ val hasInitialized: Boolean
+ get() = longPressHint != null && effectAnimator != null
- doOnStart { handleAnimationStart() }
- addUpdateListener { _effectProgress.value = animatedValue as Float }
- doOnEnd { handleAnimationComplete() }
- doOnCancel { handleAnimationCancel() }
- }
+ @VisibleForTesting
+ fun setState(state: State) {
+ _state.value = state
+ }
private fun reverse() {
- val pausedProgress = effectAnimator.animatedFraction
- val effect =
- LongPressHapticBuilder.createReversedEffect(
- pausedProgress,
- durations?.get(0) ?: 0,
- effectDuration,
- )
- vibratorHelper?.cancel()
- vibrate(effect)
- effectAnimator.reverse()
+ effectAnimator?.let {
+ val pausedProgress = it.animatedFraction
+ val effect =
+ LongPressHapticBuilder.createReversedEffect(
+ pausedProgress,
+ durations?.get(0) ?: 0,
+ effectDuration,
+ )
+ vibratorHelper?.cancel()
+ vibrate(effect)
+ it.reverse()
+ }
}
private fun vibrate(effect: VibrationEffect?) {
@@ -129,52 +146,37 @@
}
private fun handleActionDown() {
- when (state) {
+ when (_state.value) {
State.IDLE -> {
- startPressedTimeoutWait()
- state = State.TIMEOUT_WAIT
+ setState(State.TIMEOUT_WAIT)
}
- State.RUNNING_BACKWARDS -> effectAnimator.cancel()
+ State.RUNNING_BACKWARDS -> effectAnimator?.cancel()
else -> {}
}
}
- private fun startPressedTimeoutWait() {
- waitJob =
- scope?.launch {
- try {
- delay(PRESSED_TIMEOUT)
- handleTimeoutComplete()
- } catch (_: CancellationException) {
- state = State.IDLE
- }
- }
- }
-
private fun handleActionUp() {
- when (state) {
+ when (_state.value) {
State.TIMEOUT_WAIT -> {
- waitJob?.cancel()
- _actionType.value = ActionType.CLICK
- state = State.IDLE
+ _postedActionType.value = ActionType.CLICK
+ setState(State.IDLE)
}
State.RUNNING_FORWARD -> {
reverse()
- state = State.RUNNING_BACKWARDS
+ setState(State.RUNNING_BACKWARDS)
}
else -> {}
}
}
private fun handleActionCancel() {
- when (state) {
+ when (_state.value) {
State.TIMEOUT_WAIT -> {
- waitJob?.cancel()
- state = State.IDLE
+ setState(State.IDLE)
}
State.RUNNING_FORWARD -> {
reverse()
- state = State.RUNNING_BACKWARDS
+ setState(State.RUNNING_BACKWARDS)
}
else -> {}
}
@@ -182,54 +184,78 @@
private fun handleAnimationStart() {
vibrate(longPressHint)
- state = State.RUNNING_FORWARD
+ setState(State.RUNNING_FORWARD)
}
/** This function is called both when an animator completes or gets cancelled */
private fun handleAnimationComplete() {
- if (state == State.RUNNING_FORWARD) {
+ if (_state.value == State.RUNNING_FORWARD) {
vibrate(snapEffect)
- _actionType.value = ActionType.LONG_PRESS
+ _postedActionType.value = ActionType.LONG_PRESS
_effectProgress.value = null
}
- if (state != State.TIMEOUT_WAIT) {
+ if (_state.value != State.TIMEOUT_WAIT) {
// This will happen if the animator did not finish by being cancelled
- state = State.IDLE
+ setState(State.IDLE)
}
}
private fun handleAnimationCancel() {
- _effectProgress.value = 0f
- startPressedTimeoutWait()
- state = State.TIMEOUT_WAIT
+ _effectProgress.value = null
+ setState(State.TIMEOUT_WAIT)
}
- private fun handleTimeoutComplete() {
- if (state == State.TIMEOUT_WAIT && !effectAnimator.isRunning) {
- effectAnimator.start()
+ fun handleTimeoutComplete() {
+ if (_state.value == State.TIMEOUT_WAIT && effectAnimator?.isRunning == false) {
+ effectAnimator?.start()
}
}
fun clearActionType() {
- _actionType.value = null
+ _postedActionType.value = null
+ }
+
+ /** Reset the effect by going back to a default [IDLE] state */
+ fun resetEffect() {
+ if (effectAnimator?.isRunning == true) {
+ effectAnimator?.cancel()
+ }
+ longPressHint = null
+ effectAnimator = null
+ _effectProgress.value = null
+ _postedActionType.value = null
+ setState(State.IDLE)
}
/**
* Reset the effect with a new effect duration.
*
- * The effect will go back to an [IDLE] state where it can begin its logic with a new duration.
- *
* @param[duration] New duration for the long-press effect
+ * @return true if the effect initialized correctly
*/
- fun resetWithDuration(duration: Int) {
+ fun initializeEffect(duration: Int): Boolean {
// The effect can't reset if it is running
- if (effectAnimator.isRunning) return
+ if (duration <= 0) return false
- effectAnimator.duration = duration.toLong()
- _effectProgress.value = 0f
- _actionType.value = null
- waitJob?.cancel()
- state = State.IDLE
+ resetEffect()
+ effectDuration = duration
+ effectAnimator =
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ this.duration = effectDuration.toLong()
+ interpolator = AccelerateDecelerateInterpolator()
+
+ doOnStart { handleAnimationStart() }
+ addUpdateListener { _effectProgress.value = animatedValue as Float }
+ doOnEnd { handleAnimationComplete() }
+ doOnCancel { handleAnimationCancel() }
+ }
+ longPressHint =
+ LongPressHapticBuilder.createLongPressHint(
+ durations?.get(0) ?: LongPressHapticBuilder.INVALID_DURATION,
+ durations?.get(1) ?: LongPressHapticBuilder.INVALID_DURATION,
+ effectDuration
+ )
+ return true
}
enum class State {
@@ -243,6 +269,7 @@
enum class ActionType {
CLICK,
LONG_PRESS,
+ RESET_AND_LONG_PRESS,
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
index f4998a7..ddb9f35 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
@@ -21,56 +21,68 @@
import com.android.app.tracing.coroutines.launch
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.qs.tileimpl.QSTileViewImpl
+import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.launch
-class QSLongPressEffectViewBinder {
-
- private var handle: DisposableHandle? = null
- val isBound: Boolean
- get() = handle != null
-
+// TODO(b/332903800)
+object QSLongPressEffectViewBinder {
fun bind(
tile: QSTileViewImpl,
+ qsLongPressEffect: QSLongPressEffect?,
tileSpec: String?,
- effect: QSLongPressEffect?,
- ) {
- if (effect == null) return
+ ): DisposableHandle? {
+ if (qsLongPressEffect == null) return null
- handle =
- tile.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- effect.scope = this
- val tag = "${tileSpec ?: "unknownTileSpec"}#LongPressEffect"
-
- launch("$tag#progress") {
- effect.effectProgress.collect { progress ->
- progress?.let {
- if (it == 0f) {
- tile.bringToFront()
- }
+ return tile.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ val tag = "${tileSpec ?: "unknownTileSpec"}#LongPressEffect"
+ // Progress of the effect
+ launch("$tag#progress") {
+ qsLongPressEffect.effectProgress.collect { progress ->
+ progress?.let {
+ if (it == 0f) {
+ tile.bringToFront()
+ } else {
tile.updateLongPressEffectProperties(it)
}
}
}
+ }
- launch("$tag#action") {
- effect.actionType.collect { action ->
- action?.let {
- when (it) {
- QSLongPressEffect.ActionType.CLICK -> tile.performClick()
- QSLongPressEffect.ActionType.LONG_PRESS ->
- tile.performLongClick()
+ // Action to perform
+ launch("$tag#action") {
+ qsLongPressEffect.actionType.collect { action ->
+ action?.let {
+ when (it) {
+ QSLongPressEffect.ActionType.CLICK -> tile.performClick()
+ QSLongPressEffect.ActionType.LONG_PRESS -> tile.performLongClick()
+ QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS -> {
+ tile.resetLongPressEffectProperties()
+ tile.performLongClick()
}
- effect.clearActionType()
}
+ qsLongPressEffect.clearActionType()
}
}
}
- }
- }
- fun dispose() {
- handle?.dispose()
- handle = null
+ // Tap timeout wait
+ launch("$tag#timeout") {
+ qsLongPressEffect.shouldWaitForTapTimeout
+ .filter { it }
+ .collect {
+ try {
+ delay(QSLongPressEffect.PRESSED_TIMEOUT)
+ qsLongPressEffect.handleTimeoutComplete()
+ } catch (_: CancellationException) {
+ qsLongPressEffect.resetEffect()
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 6b53f4e..a5d7e04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -63,6 +63,7 @@
import android.view.WindowManagerPolicyConstants;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
+import android.window.RemoteTransitionStub;
import android.window.TransitionInfo;
import com.android.internal.annotations.GuardedBy;
@@ -187,7 +188,7 @@
// Note: Also used for wrapping occlude by Dream animation. It works (with some redundancy).
public static IRemoteTransition wrap(final KeyguardViewMediator keyguardViewMediator,
final IRemoteAnimationRunner runner) {
- return new IRemoteTransition.Stub() {
+ return new RemoteTransitionStub() {
@GuardedBy("mLeashMap")
private final ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = new ArrayMap<>();
@@ -253,11 +254,6 @@
}
}
- @Override
- public void onTransitionConsumed(IBinder transition, boolean aborted) {
- // No-op.
- }
-
private static void initAlphaForAnimationTargets(@NonNull SurfaceControl.Transaction t,
@NonNull RemoteAnimationTarget[] targets) {
for (RemoteAnimationTarget target : targets) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index e6e6ff6..c32c226 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -41,7 +41,6 @@
import com.android.systemui.CoreStartable
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
@@ -73,7 +72,6 @@
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import dagger.Lazy
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -110,7 +108,6 @@
private val keyguardBlueprintViewBinder: KeyguardBlueprintViewBinder,
private val clockInteractor: KeyguardClockInteractor,
private val keyguardViewMediator: KeyguardViewMediator,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -214,7 +211,6 @@
vibratorHelper,
falsingManager,
keyguardViewMediator,
- mainImmediateDispatcher,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 654610e..a1ac5b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -43,7 +43,6 @@
import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground;
import static com.android.systemui.Flags.refactorGetCurrentUser;
import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -98,7 +97,6 @@
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
@@ -137,6 +135,7 @@
import com.android.systemui.animation.TransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
@@ -164,7 +163,6 @@
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -584,7 +582,7 @@
private CentralSurfaces mCentralSurfaces;
- private IRemoteAnimationFinishedCallback mUnoccludeFromDreamFinishedCallback;
+ private IRemoteAnimationFinishedCallback mUnoccludeFinishedCallback;
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
new DeviceConfig.OnPropertiesChangedListener() {
@@ -1234,10 +1232,12 @@
mUnoccludeAnimator.cancel();
}
- if (isDream) {
+ if (isDream || mShowCommunalByDefault) {
initAlphaForAnimationTargets(wallpapers);
- mDreamViewModel.get().startTransitionFromDream();
- mUnoccludeFromDreamFinishedCallback = finishedCallback;
+ if (isDream) {
+ mDreamViewModel.get().startTransitionFromDream();
+ }
+ mUnoccludeFinishedCallback = finishedCallback;
return;
}
@@ -1304,7 +1304,10 @@
private Consumer<Float> getRemoteSurfaceAlphaApplier() {
return (Float alpha) -> {
- if (mRemoteAnimationTarget == null) return;
+ if (mRemoteAnimationTarget == null) {
+ Log.e(TAG, "Attempting to set alpha on null animation target");
+ return;
+ }
final View localView = mKeyguardViewControllerLazy.get().getViewRootImpl().getView();
final SyncRtSurfaceTransactionApplier applier =
new SyncRtSurfaceTransactionApplier(localView);
@@ -1319,10 +1322,10 @@
private Consumer<TransitionStep> getFinishedCallbackConsumer() {
return (TransitionStep step) -> {
- if (mUnoccludeFromDreamFinishedCallback == null) return;
+ if (mUnoccludeFinishedCallback == null) return;
try {
- mUnoccludeFromDreamFinishedCallback.onAnimationFinished();
- mUnoccludeFromDreamFinishedCallback = null;
+ mUnoccludeFinishedCallback.onAnimationFinished();
+ mUnoccludeFinishedCallback = null;
} catch (RemoteException e) {
Log.e(TAG, "Wasn't able to callback", e);
}
@@ -1365,7 +1368,9 @@
private final SessionTracker mSessionTracker;
private final CoroutineDispatcher mMainDispatcher;
private final Lazy<DreamViewModel> mDreamViewModel;
+ private final Lazy<CommunalTransitionViewModel> mCommunalTransitionViewModel;
private RemoteAnimationTarget mRemoteAnimationTarget;
+ private boolean mShowCommunalByDefault = false;
private final Lazy<WindowManagerLockscreenVisibilityManager> mWmLockscreenVisibilityManager;
@@ -1414,6 +1419,7 @@
SystemClock systemClock,
@Main CoroutineDispatcher mainDispatcher,
Lazy<DreamViewModel> dreamViewModel,
+ Lazy<CommunalTransitionViewModel> communalTransitionViewModel,
SystemPropertiesHelper systemPropertiesHelper,
Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager,
SelectedUserInteractor selectedUserInteractor,
@@ -1485,6 +1491,7 @@
mSessionTracker = sessionTracker;
mDreamViewModel = dreamViewModel;
+ mCommunalTransitionViewModel = communalTransitionViewModel;
mWmLockscreenVisibilityManager = wmLockscreenVisibilityManager;
mMainDispatcher = mainDispatcher;
@@ -1613,14 +1620,18 @@
adjustStatusBarLocked();
mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback);
- ViewRootImpl viewRootImpl = mKeyguardViewControllerLazy.get().getViewRootImpl();
- if (viewRootImpl != null) {
- final DreamViewModel viewModel = mDreamViewModel.get();
- collectFlow(viewRootImpl.getView(), viewModel.getDreamAlpha(),
- getRemoteSurfaceAlphaApplier(), mMainDispatcher);
- collectFlow(viewRootImpl.getView(), viewModel.getTransitionEnded(),
- getFinishedCallbackConsumer(), mMainDispatcher);
- }
+ final DreamViewModel dreamViewModel = mDreamViewModel.get();
+ final CommunalTransitionViewModel communalViewModel =
+ mCommunalTransitionViewModel.get();
+
+ mJavaAdapter.alwaysCollectFlow(dreamViewModel.getDreamAlpha(),
+ getRemoteSurfaceAlphaApplier());
+ mJavaAdapter.alwaysCollectFlow(dreamViewModel.getTransitionEnded(),
+ getFinishedCallbackConsumer());
+ mJavaAdapter.alwaysCollectFlow(communalViewModel.getShowByDefault(),
+ (showByDefault) -> mShowCommunalByDefault = showByDefault);
+ mJavaAdapter.alwaysCollectFlow(communalViewModel.getTransitionFromOccludedEnded(),
+ getFinishedCallbackConsumer());
}
// Most services aren't available until the system reaches the ready state, so we
// send it here when the device first boots.
@@ -3539,15 +3550,14 @@
ShadeLockscreenInteractor shadeLockscreenInteractor,
@Nullable ShadeExpansionStateManager shadeExpansionStateManager,
BiometricUnlockController biometricUnlockController,
- View notificationContainer, KeyguardBypassController bypassController) {
+ View notificationContainer) {
mCentralSurfaces = centralSurfaces;
mKeyguardViewControllerLazy.get().registerCentralSurfaces(
centralSurfaces,
shadeLockscreenInteractor,
shadeExpansionStateManager,
biometricUnlockController,
- notificationContainer,
- bypassController);
+ notificationContainer);
return mKeyguardViewControllerLazy.get();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
index e101b0a..a65a882 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
@@ -29,6 +29,7 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.utils.GlobalWindowManager
@@ -83,7 +84,7 @@
applicationScope.launch(bgDispatcher) {
// We drop 1 to avoid triggering on initial collect().
- keyguardTransitionInteractor.anyStateToGoneTransition.collect { transition ->
+ keyguardTransitionInteractor.transition(to = GONE).collect { transition ->
if (transition.transitionState == TransitionState.FINISHED) {
onKeyguardGone()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index a243b8e..7879ab6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -39,6 +39,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
+import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -161,6 +162,7 @@
SystemClock systemClock,
@Main CoroutineDispatcher mainDispatcher,
Lazy<DreamViewModel> dreamViewModel,
+ Lazy<CommunalTransitionViewModel> communalTransitionViewModel,
SystemPropertiesHelper systemPropertiesHelper,
Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager,
SelectedUserInteractor selectedUserInteractor,
@@ -208,6 +210,7 @@
systemClock,
mainDispatcher,
dreamViewModel,
+ communalTransitionViewModel,
systemPropertiesHelper,
wmLockscreenVisibilityManager,
selectedUserInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
index 3f4d3a8..6c29bce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.data.repository
+import android.content.Context
import android.os.UserHandle
import android.provider.Settings
import com.android.keyguard.ClockEventController
@@ -24,9 +25,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.SettingsClockSize
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockId
+import com.android.systemui.res.R
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
@@ -47,7 +51,11 @@
import kotlinx.coroutines.withContext
interface KeyguardClockRepository {
- /** clock size determined by notificationPanelViewController, LARGE or SMALL */
+ /**
+ * clock size determined by notificationPanelViewController, LARGE or SMALL
+ *
+ * @deprecated When scene container flag is on use clockSize from domain level.
+ */
val clockSize: StateFlow<Int>
/** clock size selected in picker, DYNAMIC or SMALL */
@@ -61,6 +69,9 @@
val previewClock: Flow<ClockController>
val clockEventController: ClockEventController
+
+ val shouldForceSmallClock: Boolean
+
fun setClockSize(@ClockSize size: Int)
}
@@ -73,6 +84,8 @@
override val clockEventController: ClockEventController,
@Background private val backgroundDispatcher: CoroutineDispatcher,
@Application private val applicationScope: CoroutineScope,
+ @Application private val applicationContext: Context,
+ private val featureFlags: FeatureFlagsClassic,
) : KeyguardClockRepository {
/** Receive SMALL or LARGE clock should be displayed on keyguard. */
@@ -135,6 +148,12 @@
clockRegistry.createCurrentClock()
}
+ override val shouldForceSmallClock: Boolean
+ get() =
+ featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE) &&
+ // True on small landscape screens
+ applicationContext.resources.getBoolean(R.bool.force_small_clock_on_lockscreen)
+
private fun getClockSize(): SettingsClockSize {
return if (
secureSettings.getIntForUser(
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 1298fa5..462d837 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
@@ -206,7 +206,11 @@
)
val keyguardDoneAnimationsFinished: Flow<Unit>
- /** Receive whether clock should be centered on lockscreen. */
+ /**
+ * Receive whether clock should be centered on lockscreen.
+ *
+ * @deprecated When scene container flag is on use clockShouldBeCentered from domain level.
+ */
val clockShouldBeCentered: Flow<Boolean>
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
index bf1f074..eef4b97 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
@@ -27,7 +27,7 @@
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -52,7 +52,6 @@
@Application private val applicationScope: CoroutineScope,
@Application private val context: Context,
deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
- private val sceneContainerFlags: SceneContainerFlags,
private val sceneInteractor: SceneInteractor,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
alternateBouncerInteractor: AlternateBouncerInteractor,
@@ -75,7 +74,7 @@
get() = context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer)
private val isBouncerSceneActive: Flow<Boolean> =
- if (sceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
sceneInteractor.currentScene.map { it == Scenes.Bouncer }.distinctUntilChanged()
} else {
flowOf(false)
@@ -115,7 +114,7 @@
.distinctUntilChanged()
private fun isBouncerActive(): Boolean {
- if (sceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
return sceneInteractor.currentScene.value == Scenes.Bouncer
}
return primaryBouncerInteractor.isBouncerShowing() &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index bef5ee5..b8ceab3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -30,6 +30,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@@ -71,6 +72,7 @@
listenForDreamingToAodOrDozing()
listenForTransitionToCamera(scope, keyguardInteractor)
listenForDreamingToGlanceableHub()
+ listenForDreamingToPrimaryBouncer()
}
private fun listenForDreamingToGlanceableHub() {
@@ -84,6 +86,21 @@
}
}
+ private fun listenForDreamingToPrimaryBouncer() {
+ scope.launch {
+ keyguardInteractor.primaryBouncerShowing
+ .sample(startedKeyguardTransitionStep, ::Pair)
+ .collect { pair ->
+ val (isBouncerShowing, lastStartedTransitionStep) = pair
+ if (
+ isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.DREAMING
+ ) {
+ startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
+ }
+ }
+ }
+ }
+
fun startToLockscreenTransition() {
scope.launch {
KeyguardWmStateRefactor.isUnexpectedlyInLegacyMode()
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 b6289d4..ee589f4 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
@@ -87,12 +87,12 @@
scope.launch {
keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop
.filterRelevantKeyguardStateAnd { onTop -> !onTop }
- .sample(communalInteractor.isIdleOnCommunal, ::Pair)
- .collect { (_, isIdleOnCommunal) ->
+ .sample(communalInteractor.isIdleOnCommunal, communalInteractor.showByDefault)
+ .collect { (_, isIdleOnCommunal, showCommunalByDefault) ->
// Occlusion signals come from the framework, and should interrupt any
// existing transition
val to =
- if (isIdleOnCommunal) {
+ if (isIdleOnCommunal || showCommunalByDefault) {
KeyguardState.GLANCEABLE_HUB
} else {
KeyguardState.LOCKSCREEN
@@ -106,15 +106,16 @@
.sample(
keyguardInteractor.isKeyguardShowing,
communalInteractor.isIdleOnCommunal,
+ communalInteractor.showByDefault,
)
- .filterRelevantKeyguardStateAnd { (isOccluded, isShowing, _) ->
+ .filterRelevantKeyguardStateAnd { (isOccluded, isShowing, _, _) ->
!isOccluded && isShowing
}
- .collect { (_, _, isIdleOnCommunal) ->
+ .collect { (_, _, isIdleOnCommunal, showCommunalByDefault) ->
// Occlusion signals come from the framework, and should interrupt any
// existing transition
val to =
- if (isIdleOnCommunal) {
+ if (isIdleOnCommunal || showCommunalByDefault) {
KeyguardState.GLANCEABLE_HUB
} else {
KeyguardState.LOCKSCREEN
@@ -175,6 +176,7 @@
duration =
when (toState) {
KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
+ KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
else -> DEFAULT_DURATION
}.inWholeMilliseconds
}
@@ -184,6 +186,7 @@
const val TAG = "FromOccludedTransitionInteractor"
private val DEFAULT_DURATION = 500.milliseconds
val TO_LOCKSCREEN_DURATION = 933.milliseconds
+ val TO_GLANCEABLE_HUB_DURATION = 250.milliseconds
val TO_AOD_DURATION = DEFAULT_DURATION
val TO_DOZING_DURATION = DEFAULT_DURATION
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index d39bd3d..720baec 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -83,19 +83,12 @@
private fun updateBlueprint() {
val useSplitShade =
splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
- // TODO(b/326098079): Make ID a constant value.
- val useWeatherClockLayout =
- clockInteractor.currentClock.value?.config?.id == "DIGITAL_CLOCK_WEATHER" &&
- ComposeLockscreen.isEnabled
val blueprintId =
when {
- useWeatherClockLayout && useSplitShade -> SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
- useWeatherClockLayout -> WEATHER_CLOCK_BLUEPRINT_ID
useSplitShade && !ComposeLockscreen.isEnabled -> SplitShadeKeyguardBlueprint.ID
else -> DefaultKeyguardBlueprint.DEFAULT
}
-
transitionToBlueprint(blueprintId)
}
@@ -128,13 +121,4 @@
fun getCurrentBlueprint(): KeyguardBlueprint {
return keyguardBlueprintRepository.blueprint.value
}
-
- companion object {
- /**
- * These values live here because classes in the composable package do not exist in some
- * systems.
- */
- const val WEATHER_CLOCK_BLUEPRINT_ID = "weather-clock"
- const val SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID = "split-shade-weather-clock"
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
index d551c9b..f7f60a5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -21,23 +21,48 @@
import com.android.keyguard.ClockEventController
import com.android.keyguard.KeyguardClockSwitch
import com.android.keyguard.KeyguardClockSwitch.ClockSize
+import com.android.keyguard.KeyguardClockSwitch.LARGE
+import com.android.keyguard.KeyguardClockSwitch.SMALL
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardClockRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.SettingsClockSize
+import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockId
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
+import com.android.systemui.util.kotlin.combine
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
private val TAG = KeyguardClockInteractor::class.simpleName
-/** Manages and ecapsulates the clock components of the lockscreen root view. */
+/** Manages and encapsulates the clock components of the lockscreen root view. */
@SysUISingleton
class KeyguardClockInteractor
@Inject
constructor(
+ mediaCarouselInteractor: MediaCarouselInteractor,
+ activeNotificationsInteractor: ActiveNotificationsInteractor,
+ shadeInteractor: ShadeInteractor,
+ keyguardInteractor: KeyguardInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ headsUpNotificationInteractor: HeadsUpNotificationInteractor,
+ @Application private val applicationScope: CoroutineScope,
private val keyguardClockRepository: KeyguardClockRepository,
) {
+ private val isOnAod: Flow<Boolean> =
+ keyguardTransitionInteractor.currentKeyguardState.map { it == KeyguardState.AOD }
val selectedClockSize: StateFlow<SettingsClockSize> = keyguardClockRepository.selectedClockSize
@@ -51,7 +76,64 @@
var clock: ClockController? by keyguardClockRepository.clockEventController::clock
- val clockSize: StateFlow<Int> = keyguardClockRepository.clockSize
+ // TODO (b/333389512): Convert this into a more readable enum.
+ val clockSize: StateFlow<Int> =
+ if (SceneContainerFlag.isEnabled) {
+ combine(
+ shadeInteractor.shadeMode,
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ mediaCarouselInteractor.hasActiveMediaOrRecommendation,
+ keyguardInteractor.isDozing,
+ isOnAod,
+ ) { shadeMode, hasNotifs, hasMedia, isDozing, isOnAod ->
+ return@combine when {
+ keyguardClockRepository.shouldForceSmallClock && !isOnAod -> SMALL
+ shadeMode == ShadeMode.Single && (hasNotifs || hasMedia) -> SMALL
+ shadeMode == ShadeMode.Single -> LARGE
+ hasMedia && !isDozing -> SMALL
+ else -> LARGE
+ }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = LARGE
+ )
+ } else {
+ SceneContainerFlag.assertInLegacyMode()
+ keyguardClockRepository.clockSize
+ }
+
+ val clockShouldBeCentered: Flow<Boolean> =
+ if (SceneContainerFlag.isEnabled) {
+ combine(
+ shadeInteractor.shadeMode,
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ keyguardInteractor.isActiveDreamLockscreenHosted,
+ isOnAod,
+ headsUpNotificationInteractor.isHeadsUpOrAnimatingAway,
+ keyguardInteractor.isDozing,
+ ) {
+ shadeMode,
+ areAnyNotificationsPresent,
+ isActiveDreamLockscreenHosted,
+ isOnAod,
+ isHeadsUp,
+ isDozing ->
+ when {
+ shadeMode != ShadeMode.Split -> true
+ !areAnyNotificationsPresent -> true
+ isActiveDreamLockscreenHosted -> true
+ // Pulsing notification appears on the right. Move clock left to avoid overlap.
+ isHeadsUp && isDozing -> false
+ else -> isOnAod
+ }
+ }
+ } else {
+ SceneContainerFlag.assertInLegacyMode()
+ keyguardInteractor.clockShouldBeCentered
+ }
+
fun setClockSize(@ClockSize size: Int) {
keyguardClockRepository.setClockSize(size)
}
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 2182fe3..7224536 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
@@ -44,7 +44,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.CommandQueue
@@ -84,7 +84,6 @@
private val repository: KeyguardRepository,
private val commandQueue: CommandQueue,
powerInteractor: PowerInteractor,
- sceneContainerFlags: SceneContainerFlags,
bouncerRepository: KeyguardBouncerRepository,
configurationInteractor: ConfigurationInteractor,
shadeRepository: ShadeRepository,
@@ -210,7 +209,8 @@
keyguardTransitionInteractor
.transitionValue(GONE)
.map { it == 1f }
- .onStart { emit(false) },
+ .onStart { emit(false) }
+ .distinctUntilChanged(),
repository.topClippingBounds
) { _, isGone, topClippingBounds ->
if (!isGone) {
@@ -330,7 +330,7 @@
/** Whether to animate the next doze mode transition. */
val animateDozingTransitions: Flow<Boolean> by lazy {
- if (sceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
sceneInteractorProvider
.get()
.transitioningTo
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
index 80e94a2..20b7b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
@@ -23,12 +23,14 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
+import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.kotlin.toPx
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
/**
* Distance over which the surface behind the keyguard is animated in during a Y-translation
@@ -96,13 +98,21 @@
.distinctUntilChanged()
/**
+ * Whether a notification launch animation is running when we're not already in the GONE state.
+ */
+ private val isNotificationLaunchAnimationRunningOnKeyguard =
+ notificationLaunchInteractor.isLaunchAnimationRunning
+ .sample(transitionInteractor.finishedKeyguardState)
+ .map { it != KeyguardState.GONE }
+
+ /**
* Whether we're animating the surface, or a notification launch animation is running (which
* means we're going to animate the surface, even if animators aren't yet running).
*/
val isAnimatingSurface =
combine(
repository.isAnimatingSurface,
- notificationLaunchInteractor.isLaunchAnimationRunning
+ isNotificationLaunchAnimationRunningOnKeyguard,
) { animatingSurface, animatingLaunch ->
animatingSurface || animatingLaunch
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 97081d9..a18579d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -20,19 +20,14 @@
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
-import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
-import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
-import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
-import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionInfo
@@ -40,7 +35,6 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.BufferOverflow
@@ -53,6 +47,7 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -64,7 +59,6 @@
@Inject
constructor(
@Application val scope: CoroutineScope,
- @Main private val mainDispatcher: CoroutineDispatcher,
private val keyguardRepository: KeyguardRepository,
private val repository: KeyguardTransitionRepository,
private val fromLockscreenTransitionInteractor: dagger.Lazy<FromLockscreenTransitionInteractor>,
@@ -75,14 +69,13 @@
dagger.Lazy<FromAlternateBouncerTransitionInteractor>,
private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>,
) {
- private val TAG = this::class.simpleName
-
- private val transitionValueCache = mutableMapOf<KeyguardState, MutableSharedFlow<Float>>()
+ private val transitionMap = mutableMapOf<Edge, MutableSharedFlow<TransitionStep>>()
/**
* Numerous flows are derived from, or care directly about, the transition value in and out of a
* single state. This prevent the redundant filters from running.
*/
+ private val transitionValueCache = mutableMapOf<KeyguardState, MutableSharedFlow<Float>>()
private fun getTransitionValueFlow(state: KeyguardState): MutableSharedFlow<Float> {
return transitionValueCache.getOrPut(state) {
MutableSharedFlow<Float>(
@@ -94,6 +87,7 @@
}
}
+ @Deprecated("Not performant - Use something else in this class")
val transitions = repository.transitions
/**
@@ -106,14 +100,14 @@
* from when we were canceled.
*/
val startedStepWithPrecedingStep =
- transitions
+ repository.transitions
.pairwise()
.filter { it.newValue.transitionState == TransitionState.STARTED }
.shareIn(scope, SharingStarted.Eagerly)
init {
// Collect non-canceled steps and emit transition values.
- scope.launch(mainDispatcher) {
+ scope.launch {
repository.transitions
.filter { it.transitionState != TransitionState.CANCELED }
.collect { step ->
@@ -122,11 +116,22 @@
}
}
+ scope.launch {
+ repository.transitions.collect {
+ // FROM->TO
+ transitionMap[Edge(it.from, it.to)]?.emit(it)
+ // FROM->(ANY)
+ transitionMap[Edge(it.from, null)]?.emit(it)
+ // (ANY)->TO
+ transitionMap[Edge(null, it.to)]?.emit(it)
+ }
+ }
+
// If a transition from state A -> B is canceled in favor of a transition from B -> C, we
// need to ensure we emit transitionValue(A) = 0f, since no further steps will be emitted
// where the from or to states are A. This would leave transitionValue(A) stuck at an
// arbitrary non-zero value.
- scope.launch(mainDispatcher) {
+ scope.launch {
startedStepWithPrecedingStep.collect { (prevStep, startedStep) ->
if (
prevStep.transitionState == TransitionState.CANCELED &&
@@ -138,116 +143,42 @@
}
}
- /** (any)->GONE transition information */
- val anyStateToGoneTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == GONE }
-
- /** (any)->AOD transition information */
- val anyStateToAodTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == AOD }
-
- /** DREAMING->(any) transition information. */
- val fromDreamingTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.from == DREAMING }
-
- /** LOCKSCREEN->(any) transition information. */
- val fromLockscreenTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.from == LOCKSCREEN }
-
- /** (any)->Lockscreen transition information */
- val anyStateToLockscreenTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == LOCKSCREEN }
-
- /** (any)->Occluded transition information */
- val anyStateToOccludedTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == OCCLUDED }
-
- /** (any)->PrimaryBouncer transition information */
- val anyStateToPrimaryBouncerTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == PRIMARY_BOUNCER }
-
- /** (any)->Dreaming transition information */
- val anyStateToDreamingTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == DREAMING }
-
- /** (any)->AlternateBouncer transition information */
- val anyStateToAlternateBouncerTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == ALTERNATE_BOUNCER }
-
- /** AOD->LOCKSCREEN transition information. */
- val aodToLockscreenTransition: Flow<TransitionStep> = repository.transition(AOD, LOCKSCREEN)
-
- /** DREAMING->LOCKSCREEN transition information. */
- val dreamingToLockscreenTransition: Flow<TransitionStep> =
- repository.transition(DREAMING, LOCKSCREEN)
-
- /** DREAMING_LOCKSCREEN_HOSTED->LOCKSCREEN transition information. */
- val dreamingLockscreenHostedToLockscreenTransition: Flow<TransitionStep> =
- repository.transition(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN)
-
- /** GONE->AOD transition information. */
- val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD)
-
- /** GONE->DREAMING transition information. */
- val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)
-
- /** GONE->DREAMING_LOCKSCREEN_HOSTED transition information. */
- val goneToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
- repository.transition(GONE, DREAMING_LOCKSCREEN_HOSTED)
-
- /** GONE->LOCKSCREEN transition information. */
- val goneToLockscreenTransition: Flow<TransitionStep> = repository.transition(GONE, LOCKSCREEN)
-
- /** LOCKSCREEN->AOD transition information. */
- val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
-
- /** LOCKSCREEN->DOZING transition information. */
- val lockscreenToDozingTransition: Flow<TransitionStep> =
- repository.transition(LOCKSCREEN, DOZING)
-
- /** LOCKSCREEN->DREAMING transition information. */
- val lockscreenToDreamingTransition: Flow<TransitionStep> =
- repository.transition(LOCKSCREEN, DREAMING)
-
- /** LOCKSCREEN->DREAMING_LOCKSCREEN_HOSTED transition information. */
- val lockscreenToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
- repository.transition(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED)
-
- /** LOCKSCREEN->GLANCEABLE_HUB transition information. */
- val lockscreenToGlanceableHubTransition: Flow<TransitionStep> =
- repository.transition(LOCKSCREEN, GLANCEABLE_HUB)
-
- /** LOCKSCREEN->OCCLUDED transition information. */
- val lockscreenToOccludedTransition: Flow<TransitionStep> =
- repository.transition(LOCKSCREEN, OCCLUDED)
-
- /** GLANCEABLE_HUB->LOCKSCREEN transition information. */
- val glanceableHubToLockscreenTransition: Flow<TransitionStep> =
- repository.transition(GLANCEABLE_HUB, LOCKSCREEN)
-
- /** OCCLUDED->LOCKSCREEN transition information. */
- val occludedToLockscreenTransition: Flow<TransitionStep> =
- repository.transition(OCCLUDED, LOCKSCREEN)
-
- /** PRIMARY_BOUNCER->GONE transition information. */
- val primaryBouncerToGoneTransition: Flow<TransitionStep> =
- repository.transition(PRIMARY_BOUNCER, GONE)
-
- /** OFF->LOCKSCREEN transition information. */
- val offToLockscreenTransition: Flow<TransitionStep> = repository.transition(OFF, LOCKSCREEN)
-
- /** DOZING->LOCKSCREEN transition information. */
- val dozingToLockscreenTransition: Flow<TransitionStep> =
- repository.transition(DOZING, LOCKSCREEN)
-
- /** Receive all [TransitionStep] matching a filter of [from]->[to] */
- fun transition(from: KeyguardState, to: KeyguardState): Flow<TransitionStep> {
- return repository.transition(from, to)
+ /** Given an [edge], return a SharedFlow to collect only relevant [TransitionStep]. */
+ fun getOrCreateFlow(edge: Edge): MutableSharedFlow<TransitionStep> {
+ return transitionMap.getOrPut(edge) {
+ MutableSharedFlow<TransitionStep>(
+ extraBufferCapacity = 10,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
+ }
}
/**
- * AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
- * Lockscreen (0f).
+ * Receive all [TransitionStep] matching a filter of [from]->[to]. Allow nulls in order to match
+ * any transition, for instance (any)->GONE.
+ */
+ fun transition(from: KeyguardState? = null, to: KeyguardState? = null): Flow<TransitionStep> {
+ if (from == null && to == null) {
+ throw IllegalArgumentException("from and to cannot both be null")
+ }
+ return getOrCreateFlow(Edge(from = from, to = to))
+ }
+
+ /**
+ * The amount of transition into or out of the given [KeyguardState].
+ *
+ * The value will be `0` (or close to `0`, due to float point arithmetic) if not in this step or
+ * `1` when fully in the given state.
+ */
+ fun transitionValue(
+ state: KeyguardState,
+ ): Flow<Float> {
+ return getTransitionValueFlow(state)
+ }
+
+ /**
+ * AOD<->* transition information, mapped to dozeAmount range of AOD (1f) <->
+ * * (0f).
*/
val dozeAmountTransition: Flow<TransitionStep> =
repository.transitions
@@ -265,10 +196,6 @@
val startedKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
- /** The last [TransitionStep] with a [TransitionState] of CANCELED */
- val canceledKeyguardTransitionStep: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.transitionState == TransitionState.CANCELED }
-
/** The last [TransitionStep] with a [TransitionState] of FINISHED */
val finishedKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
@@ -364,10 +291,6 @@
* case, the smartspace will never be set to alpha = 1f and you'll have a half-faded smartspace
* during the LS -> GONE transition.
*
- * If you need special-case handling for cancellations (such as conditional handling depending
- * on which [KeyguardState] was canceled) you can collect [canceledKeyguardTransitionStep]
- * directly.
- *
* As a helpful footnote, here's the values of [finishedKeyguardState] and
* [currentKeyguardState] during a sequence with two cancellations:
* 1. We're FINISHED in GONE. currentKeyguardState=GONE; finishedKeyguardState=GONE.
@@ -390,7 +313,7 @@
}
}
.distinctUntilChanged()
- .shareIn(scope, SharingStarted.Eagerly, replay = 1)
+ .stateIn(scope, SharingStarted.Eagerly, KeyguardState.OFF)
/**
* The [TransitionInfo] of the most recent call to
@@ -420,24 +343,12 @@
/** Whether we've currently STARTED a transition and haven't yet FINISHED it. */
val isInTransitionToAnyState = isInTransitionWhere({ true }, { true })
- /**
- * The amount of transition into or out of the given [KeyguardState].
- *
- * The value will be `0` (or close to `0`, due to float point arithmetic) if not in this step or
- * `1` when fully in the given state.
- */
- fun transitionValue(
- state: KeyguardState,
- ): Flow<Float> {
- return getTransitionValueFlow(state)
- }
-
fun transitionStepsFromState(fromState: KeyguardState): Flow<TransitionStep> {
- return repository.transitions.filter { step -> step.from == fromState }
+ return getOrCreateFlow(Edge(from = fromState, to = null))
}
fun transitionStepsToState(toState: KeyguardState): Flow<TransitionStep> {
- return repository.transitions.filter { step -> step.to == toState }
+ return getOrCreateFlow(Edge(from = null, to = toState))
}
/**
@@ -464,17 +375,24 @@
fun isInTransitionToState(
state: KeyguardState,
): Flow<Boolean> {
- return isInTransitionToStateWhere { it == state }
+ return getOrCreateFlow(Edge(from = null, to = state))
+ .mapLatest { it.transitionState.isTransitioning() }
+ .onStart { emit(false) }
+ .distinctUntilChanged()
}
/**
- * Whether we're in a transition to a [KeyguardState] that matches the given predicate, but
- * haven't yet completed it.
+ * Whether we're in a transition to and from the given [KeyguardState]s, but haven't yet
+ * completed it.
*/
- fun isInTransitionToStateWhere(
- stateMatcher: (KeyguardState) -> Boolean,
+ fun isInTransition(
+ from: KeyguardState,
+ to: KeyguardState,
): Flow<Boolean> {
- return isInTransitionWhere(fromStatePredicate = { true }, toStatePredicate = stateMatcher)
+ return getOrCreateFlow(Edge(from = from, to = to))
+ .mapLatest { it.transitionState.isTransitioning() }
+ .onStart { emit(false) }
+ .distinctUntilChanged()
}
/**
@@ -483,12 +401,29 @@
fun isInTransitionFromState(
state: KeyguardState,
): Flow<Boolean> {
- return isInTransitionFromStateWhere { it == state }
+ return getOrCreateFlow(Edge(from = state, to = null))
+ .mapLatest { it.transitionState.isTransitioning() }
+ .onStart { emit(false) }
+ .distinctUntilChanged()
+ }
+
+ /**
+ * Whether we're in a transition to a [KeyguardState] that matches the given predicate, but
+ * haven't yet completed it.
+ *
+ * If you only care about a single state, instead use the optimized [isInTransitionToState].
+ */
+ fun isInTransitionToStateWhere(
+ stateMatcher: (KeyguardState) -> Boolean,
+ ): Flow<Boolean> {
+ return isInTransitionWhere(fromStatePredicate = { true }, toStatePredicate = stateMatcher)
}
/**
* Whether we're in a transition out of a [KeyguardState] that matches the given predicate, but
* haven't yet completed it.
+ *
+ * If you only care about a single state, instead use the optimized [isInTransitionFromState].
*/
fun isInTransitionFromStateWhere(
stateMatcher: (KeyguardState) -> Boolean,
@@ -499,6 +434,9 @@
/**
* Whether we're in a transition between two [KeyguardState]s that match the given predicates,
* but haven't yet completed it.
+ *
+ * If you only care about a single state for both from and to, instead use the optimized
+ * [isInTransition].
*/
fun isInTransitionWhere(
fromStatePredicate: (KeyguardState) -> Boolean,
@@ -507,6 +445,13 @@
return isInTransitionWhere { from, to -> fromStatePredicate(from) && toStatePredicate(to) }
}
+ /**
+ * Whether we're in a transition between two [KeyguardState]s that match the given predicates,
+ * but haven't yet completed it.
+ *
+ * If you only care about a single state for both from and to, instead use the optimized
+ * [isInTransition].
+ */
fun isInTransitionWhere(
fromToStatePredicate: (KeyguardState, KeyguardState) -> Boolean
): Flow<Boolean> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/RefactorKeyguardDismissIntent.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/RefactorKeyguardDismissIntent.kt
new file mode 100644
index 0000000..a43eb71
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/RefactorKeyguardDismissIntent.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the refactor_keyguard_dismiss_intent flag. */
+@Suppress("NOTHING_TO_INLINE")
+object RefactorKeyguardDismissIntent {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.refactorKeyguardDismissIntent()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
index 38a93b5..1cd188c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
@@ -18,11 +18,21 @@
/** Possible states for a running transition between [State] */
enum class TransitionState {
/* Transition has begun. */
- STARTED,
+ STARTED {
+ override fun isTransitioning() = true
+ },
/* Transition is actively running. */
- RUNNING,
+ RUNNING {
+ override fun isTransitioning() = true
+ },
/* Transition has completed successfully. */
- FINISHED,
+ FINISHED {
+ override fun isTransitioning() = false
+ },
/* Transition has been interrupted, and not completed successfully. */
- CANCELED,
+ CANCELED {
+ override fun isTransitioning() = false
+ };
+
+ abstract fun isTransitioning(): Boolean
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 5de1a61..735b109 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -19,8 +19,6 @@
import com.android.app.animation.Interpolators.LINEAR
import com.android.keyguard.logging.KeyguardTransitionAnimationLogger
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -35,15 +33,10 @@
import kotlin.math.min
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
-import kotlinx.coroutines.launch
/**
* Assists in creating sub-flows for a KeyguardTransition. Call [setup] once for a transition, and
@@ -53,35 +46,9 @@
class KeyguardTransitionAnimationFlow
@Inject
constructor(
- @Application private val scope: CoroutineScope,
- @Main private val mainDispatcher: CoroutineDispatcher,
private val transitionInteractor: KeyguardTransitionInteractor,
private val logger: KeyguardTransitionAnimationLogger,
) {
- private val transitionMap = mutableMapOf<Edge, MutableSharedFlow<TransitionStep>>()
-
- init {
- scope.launch(mainDispatcher) {
- transitionInteractor.transitions.collect {
- // FROM->TO
- transitionMap[Edge(it.from, it.to)]?.emit(it)
- // FROM->(ANY)
- transitionMap[Edge(it.from, null)]?.emit(it)
- // (ANY)->TO
- transitionMap[Edge(null, it.to)]?.emit(it)
- }
- }
- }
-
- private fun getOrCreateFlow(edge: Edge): MutableSharedFlow<TransitionStep> {
- return transitionMap.getOrPut(edge) {
- MutableSharedFlow<TransitionStep>(
- extraBufferCapacity = 10,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
- )
- }
- }
-
/** Invoke once per transition between FROM->TO states to get access to a shared flow. */
fun setup(
duration: Duration,
@@ -185,7 +152,8 @@
}?.let { onStep(interpolator.getInterpolation(it)) }
}
- return getOrCreateFlow(edge)
+ return transitionInteractor
+ .getOrCreateFlow(edge)
.map { step ->
StateToValue(
from = step.from,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AccessibilityActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AccessibilityActionsViewBinder.kt
new file mode 100644
index 0000000..8f5a6a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AccessibilityActionsViewBinder.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.os.Bundle
+import android.view.View
+import android.view.accessibility.AccessibilityNodeInfo
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.keyguard.ui.viewmodel.AccessibilityActionsViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.launch
+
+/** View binder for accessibility actions placeholder on keyguard. */
+object AccessibilityActionsViewBinder {
+ fun bind(
+ view: View,
+ viewModel: AccessibilityActionsViewModel,
+ ): DisposableHandle {
+ val disposableHandle =
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ view.contentDescription =
+ view.resources.getString(R.string.accessibility_desc_lock_screen)
+
+ launch {
+ viewModel.isOnKeyguard.collect { isOnKeyguard ->
+ view.importantForAccessibility =
+ if (isOnKeyguard) {
+ View.IMPORTANT_FOR_ACCESSIBILITY_YES
+ } else {
+ // The border won't be displayed when keyguard is not showing or
+ // when the focus was previously on it but is now transitioning
+ // away from the keyguard.
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ }
+ }
+ }
+
+ launch {
+ viewModel.isCommunalAvailable.collect { canOpenGlanceableHub ->
+ view.accessibilityDelegate =
+ object : View.AccessibilityDelegate() {
+ override fun onInitializeAccessibilityNodeInfo(
+ host: View,
+ info: AccessibilityNodeInfo
+ ) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ // Add custom actions
+ if (canOpenGlanceableHub) {
+ val action =
+ AccessibilityNodeInfo.AccessibilityAction(
+ R.id.accessibility_action_open_communal_hub,
+ view.resources.getString(
+ R.string
+ .accessibility_action_open_communal_hub
+ ),
+ )
+ info.addAction(action)
+ }
+ }
+
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ return if (
+ action == R.id.accessibility_action_open_communal_hub
+ ) {
+ viewModel.openCommunalHub()
+ true
+ } else super.performAccessibilityAction(host, action, args)
+ }
+ }
+ }
+ }
+ }
+ }
+ return disposableHandle
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index e423fe0..f46a207 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -25,7 +25,6 @@
import androidx.core.view.isInvisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
import com.android.systemui.common.ui.view.LongPressHandlingView
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
@@ -35,15 +34,13 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.VibratorHelper
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
@ExperimentalCoroutinesApi
object DeviceEntryIconViewBinder {
- private const val TAG = "DeviceEntryIconViewBinder"
-
/**
* Updates UI for:
* - device entry containing view (parent view for the below views)
@@ -61,7 +58,6 @@
bgViewModel: DeviceEntryBackgroundViewModel,
falsingManager: FalsingManager,
vibratorHelper: VibratorHelper,
- mainImmediateDispatcher: CoroutineDispatcher,
) {
DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
val longPressHandlingView = view.longPressHandlingView
@@ -77,33 +73,31 @@
view,
HapticFeedbackConstants.CONFIRM,
)
- applicationScope.launch("$TAG#viewModel.onLongPress") {
- viewModel.onLongPress()
- }
+ applicationScope.launch { viewModel.onLongPress() }
}
}
- view.repeatWhenAttached(mainImmediateDispatcher) {
+ view.repeatWhenAttached {
// Repeat on CREATED so that the view will always observe the entire
// GONE => AOD transition (even though the view may not be visible until the middle
// of the transition.
repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch("$TAG#viewModel.isVisible") {
+ launch {
viewModel.isVisible.collect { isVisible ->
longPressHandlingView.isInvisible = !isVisible
}
}
- launch("$TAG#viewModel.isLongPressEnabled") {
+ launch {
viewModel.isLongPressEnabled.collect { isEnabled ->
longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
}
}
- launch("$TAG#viewModel.accessibilityDelegateHint") {
+ launch {
viewModel.accessibilityDelegateHint.collect { hint ->
view.accessibilityHintType = hint
}
}
- launch("$TAG#viewModel.useBackgroundProtection") {
+ launch {
viewModel.useBackgroundProtection.collect { useBackgroundProtection ->
if (useBackgroundProtection) {
bgView.visibility = View.VISIBLE
@@ -112,7 +106,7 @@
}
}
}
- launch("$TAG#viewModel.burnInOffsets") {
+ launch {
viewModel.burnInOffsets.collect { burnInOffsets ->
view.translationX = burnInOffsets.x.toFloat()
view.translationY = burnInOffsets.y.toFloat()
@@ -120,17 +114,15 @@
}
}
- launch("$TAG#viewModel.deviceEntryViewAlpha") {
- viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha }
- }
+ launch { viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha } }
}
}
- fgIconView.repeatWhenAttached(mainImmediateDispatcher) {
+ fgIconView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// Start with an empty state
fgIconView.setImageState(StateSet.NOTHING, /* merge */ false)
- launch("$TAG#fgViewModel.viewModel") {
+ launch {
fgViewModel.viewModel.collect { viewModel ->
fgIconView.setImageState(
view.getIconState(viewModel.type, viewModel.useAodVariant),
@@ -150,10 +142,8 @@
bgView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch("$TAG#bgViewModel.alpha") {
- bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha }
- }
- launch("$TAG#bgViewModel.color") {
+ launch { bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha } }
+ launch {
bgViewModel.color.collect { color ->
bgView.imageTintList = ColorStateList.valueOf(color)
}
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 b5d6177..6b8e896 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
@@ -38,6 +38,7 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
+import com.android.systemui.shared.R as sharedR
import javax.inject.Inject
import kotlin.math.max
@@ -217,15 +218,21 @@
if (!DEBUG || currentClock == null) return
val smallClockViewId = R.id.lockscreen_clock_view
val largeClockViewId = currentClock.largeClock.layout.views[0].id
+ val smartspaceDateId = sharedR.id.date_smartspace_view
Log.i(
TAG,
"applyCsToSmallClock: vis=${cs.getVisibility(smallClockViewId)} " +
- "alpha=${cs.getConstraint(smallClockViewId).propertySet}"
+ "alpha=${cs.getConstraint(smallClockViewId).propertySet.alpha}"
)
Log.i(
TAG,
"applyCsToLargeClock: vis=${cs.getVisibility(largeClockViewId)} " +
- "alpha=${cs.getConstraint(largeClockViewId).propertySet}"
+ "alpha=${cs.getConstraint(largeClockViewId).propertySet.alpha}"
+ )
+ Log.i(
+ TAG,
+ "applyCsToSmartspaceDate: vis=${cs.getVisibility(smartspaceDateId)} " +
+ "alpha=${cs.getConstraint(smartspaceDateId).propertySet.alpha}"
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index 1b06a69..7178e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -26,7 +26,6 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
import com.android.keyguard.KeyguardClockSwitch.LARGE
import com.android.keyguard.KeyguardClockSwitch.SMALL
import com.android.systemui.keyguard.MigrateClocksToBlueprint
@@ -37,11 +36,10 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.clocks.ClockController
-import com.android.systemui.shared.clocks.DEFAULT_CLOCK_ID
-import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.launch
object KeyguardClockViewBinder {
- private const val TAG = "KeyguardClockViewBinder"
+ private val TAG = KeyguardClockViewBinder::class.simpleName!!
// When changing to new clock, we need to remove old clock views from burnInLayer
private var lastClock: ClockController? = null
@JvmStatic
@@ -51,12 +49,15 @@
viewModel: KeyguardClockViewModel,
keyguardClockInteractor: KeyguardClockInteractor,
blueprintInteractor: KeyguardBlueprintInteractor,
- ): DisposableHandle {
- keyguardClockInteractor.clockEventController.registerListeners(keyguardRootView)
-
- return keyguardRootView.repeatWhenAttached {
+ ) {
+ keyguardRootView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch("$TAG#viewModel.currentClock") {
+ keyguardClockInteractor.clockEventController.registerListeners(keyguardRootView)
+ }
+ }
+ keyguardRootView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
if (!MigrateClocksToBlueprint.isEnabled) return@launch
viewModel.currentClock.collect { currentClock ->
cleanupClockViews(currentClock, keyguardRootView, viewModel.burnInLayer)
@@ -65,22 +66,22 @@
applyConstraints(clockSection, keyguardRootView, true)
}
}
- launch("$TAG#viewModel.clockSize") {
+ launch {
if (!MigrateClocksToBlueprint.isEnabled) return@launch
viewModel.clockSize.collect {
updateBurnInLayer(keyguardRootView, viewModel)
blueprintInteractor.refreshBlueprint(Type.ClockSize)
}
}
- launch("$TAG#viewModel.clockShouldBeCentered") {
+ launch {
if (!MigrateClocksToBlueprint.isEnabled) return@launch
- viewModel.clockShouldBeCentered.collect { clockShouldBeCentered ->
+ viewModel.clockShouldBeCentered.collect {
viewModel.currentClock.value?.let {
- // Weather clock also has hasCustomPositionUpdatedAnimation as true
- // TODO(b/323020908): remove ID check
+ // TODO(b/301502635): remove "!it.config.useCustomClockScene" when
+ // migrate clocks to blueprint is fully rolled out
if (
it.largeClock.config.hasCustomPositionUpdatedAnimation &&
- it.config.id == DEFAULT_CLOCK_ID
+ !it.config.useCustomClockScene
) {
blueprintInteractor.refreshBlueprint(Type.DefaultClockStepping)
} else {
@@ -89,14 +90,11 @@
}
}
}
- launch("$TAG#viewModel.isAodIconsVisible") {
+ launch {
if (!MigrateClocksToBlueprint.isEnabled) return@launch
- viewModel.isAodIconsVisible.collect { isAodIconsVisible ->
+ viewModel.isAodIconsVisible.collect {
viewModel.currentClock.value?.let {
- // Weather clock also has hasCustomPositionUpdatedAnimation as true
- if (
- viewModel.useLargeClock && it.config.id == "DIGITAL_CLOCK_WEATHER"
- ) {
+ if (viewModel.useLargeClock && it.config.useCustomClockScene) {
blueprintInteractor.refreshBlueprint(Type.DefaultTransition)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
index d5add61..93b3ba5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
@@ -19,9 +19,8 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
+import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent
import com.android.systemui.log.core.LogLevel
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -38,11 +37,10 @@
private val interactor: KeyguardDismissActionInteractor,
@Application private val scope: CoroutineScope,
private val keyguardLogger: KeyguardLogger,
- private val featureFlags: FeatureFlagsClassic,
) : CoreStartable {
override fun start() {
- if (!featureFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ if (!RefactorKeyguardDismissIntent.isEnabled) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
index 87d8164..f77d012 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
@@ -21,8 +21,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor
+import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.log.core.LogLevel
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -44,7 +44,7 @@
) : CoreStartable {
override fun start() {
- if (!featureFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ if (!RefactorKeyguardDismissIntent.isEnabled) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 486320a..3ff32bf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -44,6 +44,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.res.R
+import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.util.Utils
import kotlin.reflect.KSuspendFunction1
@@ -77,37 +78,42 @@
context: Context,
rootView: ConstraintLayout,
viewModel: KeyguardPreviewClockViewModel,
+ clockRegistry: ClockRegistry,
updateClockAppearance: KSuspendFunction1<ClockController, Unit>,
) {
rootView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
+ var lastClock: ClockController? = null
launch("$TAG#viewModel.previewClock") {
- var lastClock: ClockController? = null
- viewModel.previewClock.collect { currentClock ->
- lastClock?.let { clock ->
- (clock.largeClock.layout.views + clock.smallClock.layout.views)
- .forEach { rootView.removeView(it) }
- }
- lastClock = currentClock
- updateClockAppearance(currentClock)
+ viewModel.previewClock.collect { currentClock ->
+ lastClock?.let { clock ->
+ (clock.largeClock.layout.views + clock.smallClock.layout.views)
+ .forEach { rootView.removeView(it) }
+ }
+ lastClock = currentClock
+ updateClockAppearance(currentClock)
- if (viewModel.shouldHighlightSelectedAffordance) {
- (currentClock.largeClock.layout.views +
- currentClock.smallClock.layout.views)
- .forEach { it.alpha = KeyguardPreviewRenderer.DIM_ALPHA }
- }
- currentClock.largeClock.layout.views.forEach {
- (it.parent as? ViewGroup)?.removeView(it)
- rootView.addView(it)
- }
+ if (viewModel.shouldHighlightSelectedAffordance) {
+ (currentClock.largeClock.layout.views +
+ currentClock.smallClock.layout.views)
+ .forEach { it.alpha = KeyguardPreviewRenderer.DIM_ALPHA }
+ }
+ currentClock.largeClock.layout.views.forEach {
+ (it.parent as? ViewGroup)?.removeView(it)
+ rootView.addView(it)
+ }
- currentClock.smallClock.layout.views.forEach {
- (it.parent as? ViewGroup)?.removeView(it)
- rootView.addView(it)
+ currentClock.smallClock.layout.views.forEach {
+ (it.parent as? ViewGroup)?.removeView(it)
+ rootView.addView(it)
+ }
+ applyPreviewConstraints(context, rootView, currentClock, viewModel)
}
- applyPreviewConstraints(context, rootView, currentClock, viewModel)
}
- }
+ .invokeOnCompletion {
+ // recover seed color especially for Transit clock
+ lastClock?.events?.onSeedColorChanged(clockRegistry.seedColor)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
index 49ae35a..88d9074 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
@@ -32,7 +32,7 @@
@JvmStatic
fun bind(
- context: Context,
+ previewContext: Context,
smartspace: View,
splitShadePreview: Boolean,
viewModel: KeyguardPreviewSmartspaceViewModel,
@@ -46,10 +46,12 @@
SettingsClockSize.DYNAMIC ->
viewModel.getLargeClockSmartspaceTopPadding(
splitShadePreview,
+ previewContext,
)
SettingsClockSize.SMALL ->
viewModel.getSmallClockSmartspaceTopPadding(
splitShadePreview,
+ previewContext,
)
}
smartspace.setTopPadding(topPadding)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 6c21e6c..abd79ab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -30,7 +30,6 @@
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
import com.android.settingslib.Utils
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableImageView
@@ -42,11 +41,11 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.doOnEnd
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
/** This is only for a SINGLE Quick affordance */
object KeyguardQuickAffordanceViewBinder {
@@ -54,7 +53,6 @@
private const val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
private const val SCALE_SELECTED_BUTTON = 1.23f
private const val DIM_ALPHA = 0.3f
- private const val TAG = "KeyguardQuickAffordanceViewBinder"
/**
* Defines interface for an object that acts as the binding between the view and its view-model.
@@ -76,15 +74,14 @@
alpha: Flow<Float>,
falsingManager: FalsingManager?,
vibratorHelper: VibratorHelper?,
- mainImmediateDispatcher: CoroutineDispatcher,
messageDisplayer: (Int) -> Unit,
): Binding {
val button = view as ImageView
val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
val disposableHandle =
- view.repeatWhenAttached(mainImmediateDispatcher) {
+ view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch("$TAG#viewModel.collect") {
+ launch {
viewModel.collect { buttonModel ->
updateButton(
view = button,
@@ -96,7 +93,7 @@
}
}
- launch("$TAG#updateButtonAlpha") {
+ launch {
updateButtonAlpha(
view = button,
viewModel = viewModel,
@@ -104,7 +101,7 @@
)
}
- launch("$TAG#configurationBasedDimensions") {
+ launch {
configurationBasedDimensions.collect { dimensions ->
button.updateLayoutParams<ViewGroup.LayoutParams> {
width = dimensions.buttonSizePx.width
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 44fd582..cc54920 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
@@ -30,10 +30,12 @@
import android.view.ViewGroup.OnHierarchyChangeListener
import android.view.ViewPropertyAnimator
import android.view.WindowInsets
+import androidx.activity.OnBackPressedDispatcher
+import androidx.activity.OnBackPressedDispatcherOwner
+import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.systemui.Flags.newAodTransition
@@ -50,6 +52,7 @@
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.shared.ComposeLockscreen
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
@@ -73,7 +76,6 @@
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
import kotlin.math.min
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
@@ -81,6 +83,7 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
/** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -101,7 +104,6 @@
vibratorHelper: VibratorHelper?,
falsingManager: FalsingManager?,
keyguardViewMediator: KeyguardViewMediator?,
- mainImmediateDispatcher: CoroutineDispatcher,
): DisposableHandle {
val disposables = DisposableHandles()
val childViews = mutableMapOf<Int, View>()
@@ -125,9 +127,24 @@
)
disposables +=
- view.repeatWhenAttached(mainImmediateDispatcher) {
+ view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch("$TAG#occludingAppDeviceEntryMessageViewModel.message") {
+ if (ComposeLockscreen.isEnabled) {
+ view.setViewTreeOnBackPressedDispatcherOwner(
+ object : OnBackPressedDispatcherOwner {
+ override val onBackPressedDispatcher =
+ OnBackPressedDispatcher().apply {
+ setOnBackInvokedDispatcher(
+ view.viewRootImpl.onBackInvokedDispatcher
+ )
+ }
+
+ override val lifecycle: Lifecycle =
+ this@repeatWhenAttached.lifecycle
+ }
+ )
+ }
+ launch {
occludingAppDeviceEntryMessageViewModel.message.collect { biometricMessage
->
if (biometricMessage?.message != null) {
@@ -146,7 +163,7 @@
if (
KeyguardBottomAreaRefactor.isEnabled || DeviceEntryUdfpsRefactor.isEnabled
) {
- launch("$TAG#viewModel.alpha") {
+ launch {
viewModel.alpha(viewState).collect { alpha ->
view.alpha = alpha
if (KeyguardBottomAreaRefactor.isEnabled) {
@@ -158,21 +175,21 @@
}
if (MigrateClocksToBlueprint.isEnabled) {
- launch("$TAG#viewModel.burnInLayerVisibility") {
+ launch {
viewModel.burnInLayerVisibility.collect { visibility ->
childViews[burnInLayerId]?.visibility = visibility
childViews[aodNotificationIconContainerId]?.visibility = visibility
}
}
- launch("$TAG#viewModel.burnInLayerAlpha") {
+ launch {
viewModel.burnInLayerAlpha.collect { alpha ->
childViews[statusViewId]?.alpha = alpha
childViews[aodNotificationIconContainerId]?.alpha = alpha
}
}
- launch("$TAG#viewModel.topClippingBounds") {
+ launch {
val clipBounds = Rect()
viewModel.topClippingBounds.collect { clipTop ->
if (clipTop == null) {
@@ -189,13 +206,13 @@
}
}
- launch("$TAG#viewModel.lockscreenStateAlpha") {
+ launch {
viewModel.lockscreenStateAlpha(viewState).collect { alpha ->
childViews[statusViewId]?.alpha = alpha
}
}
- launch("$TAG#viewModel.translationY") {
+ launch {
// When translation happens in burnInLayer, it won't be weather clock
// large clock isn't added to burnInLayer due to its scale transition
// so we also need to add translation to it here
@@ -207,7 +224,7 @@
}
}
- launch("$TAG#viewModel.translationX") {
+ launch {
viewModel.translationX.collect { state ->
val px = state.value ?: return@collect
when {
@@ -234,7 +251,7 @@
}
}
- launch("$TAG#viewModel.scale") {
+ launch {
viewModel.scale.collect { scaleViewModel ->
if (scaleViewModel.scaleClockOnly) {
// For clocks except weather clock, we have scale transition
@@ -265,7 +282,7 @@
}
if (NotificationIconContainerRefactor.isEnabled) {
- launch("$TAG#viewModel.isNotifIconContainerVisible") {
+ launch {
val iconsAppearTranslationPx =
configuration
.getDimensionPixelSize(R.dimen.shelf_appear_translation)
@@ -282,7 +299,7 @@
}
interactionJankMonitor?.let { jankMonitor ->
- launch("$TAG#viewModel.goneToAodTransition") {
+ launch {
viewModel.goneToAodTransition.collect {
when (it.transitionState) {
TransitionState.STARTED -> {
@@ -308,7 +325,7 @@
}
}
- launch("$TAG#shadeInteractor.isAnyFullyExpanded") {
+ launch {
shadeInteractor.isAnyFullyExpanded.collect { isFullyAnyExpanded ->
view.visibility =
if (isFullyAnyExpanded) {
@@ -319,12 +336,10 @@
}
}
- launch("$TAG#burnInParams.collect") {
- burnInParams.collect { viewModel.updateBurnInParams(it) }
- }
+ launch { burnInParams.collect { viewModel.updateBurnInParams(it) } }
if (deviceEntryHapticsInteractor != null && vibratorHelper != null) {
- launch("$TAG#deviceEntryHapticsInteractor.playSuccessHaptic") {
+ launch {
deviceEntryHapticsInteractor.playSuccessHaptic.collect {
vibratorHelper.performHapticFeedback(
view,
@@ -334,7 +349,7 @@
}
}
- launch("$TAG#deviceEntryHapticsInteractor.playErrorHaptic") {
+ launch {
deviceEntryHapticsInteractor.playErrorHaptic.collect {
vibratorHelper.performHapticFeedback(
view,
@@ -589,5 +604,4 @@
private const val ID = "occluding_app_device_entry_unlock_msg"
private const val AOD_ICONS_APPEAR_DURATION: Long = 200
- private const val TAG = "KeyguardRootViewBinder"
}
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 ce1aed0..bda5be4 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
@@ -327,9 +327,12 @@
smartSpaceView = lockscreenSmartspaceController.buildAndConnectDateView(parentView)
val topPadding: Int =
- smartspaceViewModel.getLargeClockSmartspaceTopPadding(previewInSplitShade())
- val startPadding: Int = smartspaceViewModel.getSmartspaceStartPadding()
- val endPadding: Int = smartspaceViewModel.getSmartspaceEndPadding()
+ smartspaceViewModel.getLargeClockSmartspaceTopPadding(
+ previewInSplitShade(),
+ previewContext,
+ )
+ val startPadding: Int = smartspaceViewModel.getSmartspaceStartPadding(previewContext)
+ val endPadding: Int = smartspaceViewModel.getSmartspaceEndPadding(previewContext)
smartSpaceView?.let {
it.setPaddingRelative(startPadding, topPadding, endPadding, 0)
@@ -387,7 +390,6 @@
null, // device entry haptics not required for preview mode
null, // falsing manager not required for preview mode
null, // keyguard view mediator is not required for preview mode
- mainDispatcher,
)
}
rootView.addView(
@@ -411,10 +413,11 @@
setUpClock(previewContext, rootView)
if (MigrateClocksToBlueprint.isEnabled) {
KeyguardPreviewClockViewBinder.bind(
- context,
+ previewContext,
keyguardRootView,
clockViewModel,
- ::updateClockAppearance
+ clockRegistry,
+ ::updateClockAppearance,
)
} else {
KeyguardPreviewClockViewBinder.bind(
@@ -429,7 +432,7 @@
smartSpaceView?.let {
KeyguardPreviewSmartspaceViewBinder.bind(
- context,
+ previewContext,
it,
previewInSplitShade(),
smartspaceViewModel
@@ -454,7 +457,6 @@
alpha = flowOf(1f),
falsingManager = falsingManager,
vibratorHelper = vibratorHelper,
- mainImmediateDispatcher = mainDispatcher,
) { message ->
indicationController.showTransientIndication(message)
}
@@ -469,7 +471,6 @@
alpha = flowOf(1f),
falsingManager = falsingManager,
vibratorHelper = vibratorHelper,
- mainImmediateDispatcher = mainDispatcher,
) { message ->
indicationController.showTransientIndication(message)
}
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 77f7ac8..d5a9655 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
@@ -21,6 +21,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.view.layout.sections.AccessibilityActionsSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
@@ -52,6 +53,7 @@
class DefaultKeyguardBlueprint
@Inject
constructor(
+ accessibilityActionsSection: AccessibilityActionsSection,
defaultIndicationAreaSection: DefaultIndicationAreaSection,
defaultDeviceEntrySection: DefaultDeviceEntrySection,
defaultShortcutsSection: DefaultShortcutsSection,
@@ -73,6 +75,7 @@
override val sections =
listOfNotNull(
+ accessibilityActionsSection,
defaultIndicationAreaSection,
defaultShortcutsSection,
defaultAmbientIndicationAreaSection.getOrNull(),
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 b4e57cc..04ac7bf 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
@@ -17,13 +17,9 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.WEATHER_CLOCK_BLUEPRINT_ID
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
-import com.android.systemui.keyguard.shared.model.KeyguardSection
import dagger.Binds
import dagger.Module
-import dagger.Provides
import dagger.multibindings.IntoSet
@Module
@@ -45,26 +41,4 @@
abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint(
shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
): KeyguardBlueprint
-
- companion object {
- /** This is a place holder for weather clock in compose. */
- @Provides
- @IntoSet
- fun bindWeatherClockBlueprintPlaceHolder(): KeyguardBlueprint {
- return object : KeyguardBlueprint {
- override val id: String = WEATHER_CLOCK_BLUEPRINT_ID
- override val sections: List<KeyguardSection> = listOf()
- }
- }
-
- /** This is a place holder for weather clock in compose. */
- @Provides
- @IntoSet
- fun bindSplitShadeWeatherClockBlueprintPlaceHolder(): KeyguardBlueprint {
- return object : KeyguardBlueprint {
- override val id: String = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
- override val sections: List<KeyguardSection> = listOf()
- }
- }
- }
}
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 55b2381..b984a68 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
@@ -21,6 +21,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.view.layout.sections.AccessibilityActionsSection
import com.android.systemui.keyguard.ui.view.layout.sections.AlignShortcutsToUdfpsSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
@@ -47,6 +48,7 @@
class ShortcutsBesideUdfpsKeyguardBlueprint
@Inject
constructor(
+ accessibilityActionsSection: AccessibilityActionsSection,
alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
defaultIndicationAreaSection: DefaultIndicationAreaSection,
defaultDeviceEntrySection: DefaultDeviceEntrySection,
@@ -68,6 +70,7 @@
override val sections =
listOfNotNull(
+ accessibilityActionsSection,
defaultIndicationAreaSection,
alignShortcutsToUdfpsSection,
defaultAmbientIndicationAreaSection.getOrNull(),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
index 8472a9f..3447177 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
@@ -21,6 +21,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.view.layout.sections.AccessibilityActionsSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
@@ -49,6 +50,7 @@
class SplitShadeKeyguardBlueprint
@Inject
constructor(
+ accessibilityActionsSection: AccessibilityActionsSection,
defaultIndicationAreaSection: DefaultIndicationAreaSection,
defaultDeviceEntrySection: DefaultDeviceEntrySection,
defaultShortcutsSection: DefaultShortcutsSection,
@@ -70,6 +72,7 @@
override val sections =
listOfNotNull(
+ accessibilityActionsSection,
defaultIndicationAreaSection,
defaultShortcutsSection,
defaultAmbientIndicationAreaSection.getOrNull(),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AccessibilityActionsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AccessibilityActionsSection.kt
new file mode 100644
index 0000000..5e5330e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AccessibilityActionsSection.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.AccessibilityActionsViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.AccessibilityActionsViewModel
+import com.android.systemui.res.R
+import com.android.systemui.util.Utils
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+
+/**
+ * A placeholder section that provides shortcuts for navigating on the keyguard through
+ * accessibility actions.
+ */
+class AccessibilityActionsSection
+@Inject
+constructor(
+ private val context: Context,
+ private val accessibilityActionsViewModel: AccessibilityActionsViewModel,
+) : KeyguardSection() {
+ private var accessibilityActionsViewHandle: DisposableHandle? = null
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!communalEnabled(context)) {
+ return
+ }
+ val view = View(constraintLayout.context).apply { id = R.id.accessibility_actions_view }
+ constraintLayout.addView(view)
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (!communalEnabled(context)) {
+ return
+ }
+ accessibilityActionsViewHandle =
+ AccessibilityActionsViewBinder.bind(
+ constraintLayout.requireViewById(R.id.accessibility_actions_view),
+ accessibilityActionsViewModel,
+ )
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
+ val accessibilityActionsViewId = R.id.accessibility_actions_view
+ constraintSet.apply {
+ // Starts from the bottom of the status bar.
+ connect(
+ accessibilityActionsViewId,
+ ConstraintSet.TOP,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.TOP,
+ Utils.getStatusBarHeaderHeightKeyguard(context)
+ )
+ connect(
+ accessibilityActionsViewId,
+ ConstraintSet.BOTTOM,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.BOTTOM,
+ )
+ // Full width
+ connect(
+ accessibilityActionsViewId,
+ ConstraintSet.START,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.START
+ )
+ connect(
+ accessibilityActionsViewId,
+ ConstraintSet.END,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.END
+ )
+ }
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ accessibilityActionsViewHandle?.dispose()
+ accessibilityActionsViewHandle = null
+ constraintLayout.removeView(R.id.accessibility_actions_view)
+ }
+}
+
+private fun communalEnabled(context: Context): Boolean {
+ return context.resources.getBoolean(R.bool.config_communalServiceEnabled) && Flags.communalHub()
+}
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 5404729..2e96638 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
@@ -36,7 +36,6 @@
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
class AlignShortcutsToUdfpsSection
@Inject
@@ -48,7 +47,6 @@
private val falsingManager: FalsingManager,
private val indicationController: KeyguardIndicationController,
private val vibratorHelper: VibratorHelper,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
) : BaseShortcutSection() {
override fun addViews(constraintLayout: ConstraintLayout) {
if (KeyguardBottomAreaRefactor.isEnabled) {
@@ -66,7 +64,6 @@
keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
falsingManager,
vibratorHelper,
- mainImmediateDispatcher,
) {
indicationController.showTransientIndication(it)
}
@@ -77,7 +74,6 @@
keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
falsingManager,
vibratorHelper,
- mainImmediateDispatcher,
) {
indicationController.showTransientIndication(it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index e0bf815..78a1fcf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -45,7 +45,6 @@
import com.android.systemui.shared.R as sharedR
import dagger.Lazy
import javax.inject.Inject
-import kotlinx.coroutines.DisposableHandle
internal fun ConstraintSet.setVisibility(
views: Iterable<View>,
@@ -66,23 +65,19 @@
val smartspaceViewModel: KeyguardSmartspaceViewModel,
val blueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
) : KeyguardSection() {
- private var handle: DisposableHandle? = null
-
override fun addViews(constraintLayout: ConstraintLayout) {}
override fun bindData(constraintLayout: ConstraintLayout) {
if (!MigrateClocksToBlueprint.isEnabled) {
return
}
- handle?.dispose()
- handle =
- KeyguardClockViewBinder.bind(
- this,
- constraintLayout,
- keyguardClockViewModel,
- clockInteractor,
- blueprintInteractor.get()
- )
+ KeyguardClockViewBinder.bind(
+ this,
+ constraintLayout,
+ keyguardClockViewModel,
+ clockInteractor,
+ blueprintInteractor.get()
+ )
}
override fun applyConstraints(constraintSet: ConstraintSet) {
@@ -94,13 +89,7 @@
}
}
- override fun removeViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
- handle?.dispose()
- handle = null
- }
+ override fun removeViews(constraintLayout: ConstraintLayout) {}
private fun buildConstraints(
clock: ClockController,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 865e989..29041d1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -30,7 +30,6 @@
import com.android.keyguard.LockIconViewController
import com.android.systemui.biometrics.AuthController
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -48,7 +47,6 @@
import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -69,7 +67,6 @@
private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
private val falsingManager: Lazy<FalsingManager>,
private val vibratorHelper: Lazy<VibratorHelper>,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
) : KeyguardSection() {
private val deviceEntryIconViewId = R.id.device_entry_icon_view
@@ -107,7 +104,6 @@
deviceEntryBackgroundViewModel.get(),
falsingManager.get(),
vibratorHelper.get(),
- mainImmediateDispatcher,
)
}
} else {
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 27ca5cd..45b8257 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
@@ -35,7 +35,6 @@
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
class DefaultShortcutsSection
@Inject
@@ -47,7 +46,6 @@
private val falsingManager: FalsingManager,
private val indicationController: KeyguardIndicationController,
private val vibratorHelper: VibratorHelper,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
) : BaseShortcutSection() {
override fun addViews(constraintLayout: ConstraintLayout) {
if (KeyguardBottomAreaRefactor.isEnabled) {
@@ -65,7 +63,6 @@
keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
falsingManager,
vibratorHelper,
- mainImmediateDispatcher,
) {
indicationController.showTransientIndication(it)
}
@@ -76,7 +73,6 @@
keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
falsingManager,
vibratorHelper,
- mainImmediateDispatcher,
) {
indicationController.showTransientIndication(it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index eaa5e33..9ec7a65 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -18,7 +18,6 @@
import android.content.Context
import android.view.View
-import android.view.View.GONE
import android.view.ViewTreeObserver.OnGlobalLayoutListener
import androidx.constraintlayout.widget.Barrier
import androidx.constraintlayout.widget.ConstraintLayout
@@ -205,8 +204,7 @@
smartspaceController.requestSmartspaceUpdate()
constraintSet.apply {
- setVisibility(
- sharedR.id.weather_smartspace_view,
+ val weatherVisibility =
when (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) {
true -> ConstraintSet.GONE
false ->
@@ -215,11 +213,18 @@
false -> ConstraintSet.GONE
}
}
+ setVisibility(sharedR.id.weather_smartspace_view, weatherVisibility)
+ setAlpha(
+ sharedR.id.weather_smartspace_view,
+ if (weatherVisibility == ConstraintSet.VISIBLE) 1f else 0f
)
- setVisibility(
- sharedR.id.date_smartspace_view,
+ val dateVisibility =
if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) ConstraintSet.GONE
else ConstraintSet.VISIBLE
+ setVisibility(sharedR.id.date_smartspace_view, dateVisibility)
+ setAlpha(
+ sharedR.id.date_smartspace_view,
+ if (dateVisibility == ConstraintSet.VISIBLE) 1f else 0f
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index 4d3a78d..91f76a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -169,10 +169,7 @@
return@OnPreDrawListener true
}
- anim.duration = duration
- anim.startDelay = startDelay
- anim.interpolator = interpolator
- anim.addListener(
+ val listener =
object : AnimatorListenerAdapter() {
override fun onAnimationStart(anim: Animator) {
assignAnimValues("start", 0f, fromVis)
@@ -183,8 +180,21 @@
if (sendToBack) toView.translationZ = 0f
toView.viewTreeObserver.removeOnPreDrawListener(predrawCallback)
}
+
+ override fun onAnimationPause(anim: Animator) {
+ toView.viewTreeObserver.removeOnPreDrawListener(predrawCallback)
+ }
+
+ override fun onAnimationResume(anim: Animator) {
+ toView.viewTreeObserver.addOnPreDrawListener(predrawCallback)
+ }
}
- )
+
+ anim.duration = duration
+ anim.startDelay = startDelay
+ anim.interpolator = interpolator
+ anim.addListener(listener)
+ anim.addPauseListener(listener)
assignAnimValues("init", 0f, fromVis)
toView.viewTreeObserver.addOnPreDrawListener(predrawCallback)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt
new file mode 100644
index 0000000..34c1436
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import javax.inject.Inject
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+/** View model for accessibility actions placeholder on keyguard */
+class AccessibilityActionsViewModel
+@Inject
+constructor(
+ private val communalInteractor: CommunalInteractor,
+ keyguardInteractor: KeyguardInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) {
+ val isCommunalAvailable = communalInteractor.isCommunalAvailable
+
+ // Checks that we are fully in lockscreen, not transitioning to another state, and shade is not
+ // opened.
+ val isOnKeyguard =
+ combine(
+ keyguardTransitionInteractor.transitionValue(KeyguardState.LOCKSCREEN).map {
+ it == 1f
+ },
+ keyguardInteractor.statusBarState
+ ) { transitionFinishedOnLockscreen, statusBarState ->
+ transitionFinishedOnLockscreen && statusBarState == StatusBarState.KEYGUARD
+ }
+ .distinctUntilChanged()
+
+ fun openCommunalHub() = communalInteractor.changeScene(CommunalScenes.Communal)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
index 06a0c72..5a559fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -18,61 +18,29 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.graphics.Color
-import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TRANSITION_DURATION_MS
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
-import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
-import com.android.wm.shell.animation.Interpolators
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
@ExperimentalCoroutinesApi
class AlternateBouncerViewModel
@Inject
constructor(
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
- animationFlow: KeyguardTransitionAnimationFlow,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
// When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be:
private val alternateBouncerScrimAlpha = .66f
- private val toAlternateBouncerTransition =
- animationFlow
- .setup(
- duration = TRANSITION_DURATION_MS,
- from = null,
- to = ALTERNATE_BOUNCER,
- )
- .sharedFlow(
- duration = TRANSITION_DURATION_MS,
- onStep = { it },
- onFinish = { 1f },
- // Reset on cancel
- onCancel = { 0f },
- interpolator = Interpolators.FAST_OUT_SLOW_IN,
- )
- private val fromAlternateBouncerTransition =
- animationFlow
- .setup(
- TRANSITION_DURATION_MS,
- from = ALTERNATE_BOUNCER,
- to = null,
- )
- .sharedFlow(
- duration = TRANSITION_DURATION_MS,
- onStep = { 1f - it },
- // Reset on cancel
- onCancel = { 0f },
- interpolator = Interpolators.FAST_OUT_SLOW_IN,
- )
/** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */
val transitionToAlternateBouncerProgress =
- merge(fromAlternateBouncerTransition, toAlternateBouncerTransition)
+ keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER)
val forcePluginOpen: Flow<Boolean> =
transitionToAlternateBouncerProgress.map { it > 0f }.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
index 7814576..5cf100e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
@@ -19,7 +19,6 @@
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionState
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -38,12 +37,9 @@
private val deviceSupportsAlternateBouncer: Flow<Boolean> =
alternateBouncerInteractor.alternateBouncerSupported
private val isTransitioningToOrFromOrShowingAlternateBouncer: Flow<Boolean> =
- keyguardTransitionInteractor.transitions
- .map {
- it.to == KeyguardState.ALTERNATE_BOUNCER ||
- (it.from == KeyguardState.ALTERNATE_BOUNCER &&
- it.transitionState != TransitionState.FINISHED)
- }
+ keyguardTransitionInteractor
+ .transitionValue(KeyguardState.ALTERNATE_BOUNCER)
+ .map { it > 0f }
.distinctUntilChanged()
val alternateBouncerWindowRequired: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 4ddd5711..d4844e2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -29,9 +29,6 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
-import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
-import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.res.R
import javax.inject.Inject
@@ -97,9 +94,9 @@
occludedToLockscreen,
aodToLockscreen ->
val translationY =
- if (isInTransition(aodToLockscreen.transitionState)) {
+ if (aodToLockscreen.transitionState.isTransitioning()) {
aodToLockscreen.value ?: 0f
- } else if (isInTransition(goneToAod.transitionState)) {
+ } else if (goneToAod.transitionState.isTransitioning()) {
(goneToAod.value ?: 0f) + burnInModel.translationY
} else {
burnInModel.translationY + occludedToLockscreen + keyguardTranslationY
@@ -110,10 +107,6 @@
.distinctUntilChanged()
}
- private fun isInTransition(state: TransitionState): Boolean {
- return state == STARTED || state == RUNNING
- }
-
private fun burnIn(
params: BurnInParameters,
): Flow<BurnInModel> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
index fe88b81..24429fa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
@@ -18,9 +18,8 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
+import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.ScrimAlpha
@@ -44,13 +43,12 @@
private val statusBarStateController: SysuiStatusBarStateController,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
private val keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
- private val featureFlags: FeatureFlagsClassic,
private val shadeInteractor: ShadeInteractor,
private val animationFlow: KeyguardTransitionAnimationFlow,
) {
/** Common fade for scrim alpha values during *BOUNCER->GONE */
fun scrimAlpha(duration: Duration, fromState: KeyguardState): Flow<ScrimAlpha> {
- return if (featureFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ return if (RefactorKeyguardDismissIntent.isEnabled) {
keyguardDismissActionInteractor
.get()
.willAnimateDismissActionOnLockscreen
@@ -104,7 +102,7 @@
to = GONE,
)
- return shadeInteractor.shadeExpansion.flatMapLatest { shadeExpansion ->
+ return shadeInteractor.isAnyExpanded.flatMapLatest { isAnyExpanded ->
transitionAnimation
.sharedFlow(
duration = duration,
@@ -112,7 +110,7 @@
onStart = {
leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide()
willRunDismissFromKeyguard = willRunAnimationOnKeyguard()
- isShadeExpanded = shadeExpansion > 0f
+ isShadeExpanded = isAnyExpanded
},
onStep = { 1f - it },
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 49fffdd..45dca99 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -30,7 +30,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.util.kotlin.sample
import dagger.Lazy
@@ -64,7 +64,6 @@
transitionInteractor: KeyguardTransitionInteractor,
val keyguardInteractor: KeyguardInteractor,
val viewModel: AodToLockscreenTransitionViewModel,
- private val sceneContainerFlags: SceneContainerFlags,
private val keyguardViewController: Lazy<KeyguardViewController>,
private val deviceEntryInteractor: DeviceEntryInteractor,
private val deviceEntrySourceInteractor: DeviceEntrySourceInteractor,
@@ -242,7 +241,7 @@
}
suspend fun onLongPress() {
- if (sceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
deviceEntryInteractor.attemptDeviceEntry()
} else {
keyguardViewController.get().showPrimaryBouncer(/* scrim */ true)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index c9251c7..a6d3312 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -26,7 +26,6 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.shared.ComposeLockscreen
import com.android.systemui.keyguard.shared.model.SettingsClockSize
import com.android.systemui.res.R
@@ -46,11 +45,10 @@
class KeyguardClockViewModel
@Inject
constructor(
- keyguardInteractor: KeyguardInteractor,
- private val keyguardClockInteractor: KeyguardClockInteractor,
+ keyguardClockInteractor: KeyguardClockInteractor,
@Application private val applicationScope: CoroutineScope,
notifsKeyguardInteractor: NotificationsKeyguardInteractor,
- @VisibleForTesting val shadeInteractor: ShadeInteractor,
+ @get:VisibleForTesting val shadeInteractor: ShadeInteractor,
) {
var burnInLayer: Layer? = null
val useLargeClock: Boolean
@@ -99,7 +97,7 @@
)
val clockShouldBeCentered: StateFlow<Boolean> =
- keyguardInteractor.clockShouldBeCentered.stateIn(
+ keyguardClockInteractor.clockShouldBeCentered.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
initialValue = false
@@ -113,18 +111,37 @@
)
val currentClockLayout: StateFlow<ClockLayout> =
- combine(isLargeClockVisible, clockShouldBeCentered, shadeInteractor.shadeMode) {
+ combine(
isLargeClockVisible,
clockShouldBeCentered,
- shadeMode ->
+ shadeInteractor.shadeMode,
+ currentClock
+ ) { isLargeClockVisible, clockShouldBeCentered, shadeMode, currentClock ->
val shouldUseSplitShade = shadeMode == ShadeMode.Split
- when {
- shouldUseSplitShade && clockShouldBeCentered -> ClockLayout.LARGE_CLOCK
- shouldUseSplitShade && isLargeClockVisible ->
- ClockLayout.SPLIT_SHADE_LARGE_CLOCK
- shouldUseSplitShade -> ClockLayout.SPLIT_SHADE_SMALL_CLOCK
- isLargeClockVisible -> ClockLayout.LARGE_CLOCK
- else -> ClockLayout.SMALL_CLOCK
+ if (currentClock?.config?.useCustomClockScene == true) {
+ val weatherClockLayout =
+ when {
+ shouldUseSplitShade && clockShouldBeCentered ->
+ ClockLayout.WEATHER_LARGE_CLOCK
+ shouldUseSplitShade && isLargeClockVisible ->
+ ClockLayout.SPLIT_SHADE_WEATHER_LARGE_CLOCK
+ shouldUseSplitShade -> ClockLayout.SPLIT_SHADE_SMALL_CLOCK
+ isLargeClockVisible -> ClockLayout.WEATHER_LARGE_CLOCK
+ else -> ClockLayout.SMALL_CLOCK
+ }
+ weatherClockLayout
+ } else {
+ val clockLayout =
+ when {
+ shouldUseSplitShade && clockShouldBeCentered -> ClockLayout.LARGE_CLOCK
+ shouldUseSplitShade && isLargeClockVisible ->
+ ClockLayout.SPLIT_SHADE_LARGE_CLOCK
+ shouldUseSplitShade -> ClockLayout.SPLIT_SHADE_SMALL_CLOCK
+ isLargeClockVisible -> ClockLayout.LARGE_CLOCK
+ else -> ClockLayout.SMALL_CLOCK
+ }
+
+ clockLayout
}
}
.stateIn(
@@ -179,5 +196,7 @@
SMALL_CLOCK,
SPLIT_SHADE_LARGE_CLOCK,
SPLIT_SHADE_SMALL_CLOCK,
+ WEATHER_LARGE_CLOCK,
+ SPLIT_SHADE_WEATHER_LARGE_CLOCK,
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockViewModel.kt
index 4f2c6f5..7300152 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockViewModel.kt
@@ -16,13 +16,10 @@
package com.android.systemui.keyguard.ui.viewmodel
-import android.content.Context
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.SettingsClockSize
import com.android.systemui.plugins.clocks.ClockController
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
@@ -31,9 +28,7 @@
class KeyguardPreviewClockViewModel
@Inject
constructor(
- @Application private val context: Context,
interactor: KeyguardClockInteractor,
- @Application private val applicationScope: CoroutineScope,
) {
var shouldHighlightSelectedAffordance: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
index b57e3ec..528b14c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.Context
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.SettingsClockSize
import com.android.systemui.res.R
@@ -31,7 +30,6 @@
class KeyguardPreviewSmartspaceViewModel
@Inject
constructor(
- @Application private val context: Context,
interactor: KeyguardClockInteractor,
val smartspaceViewModel: KeyguardSmartspaceViewModel,
val clockViewModel: KeyguardClockViewModel,
@@ -55,29 +53,29 @@
}
}
- fun getSmartspaceStartPadding(): Int {
+ fun getSmartspaceStartPadding(context: Context): Int {
return KeyguardSmartspaceViewModel.getSmartspaceStartMargin(context)
}
- fun getSmartspaceEndPadding(): Int {
+ fun getSmartspaceEndPadding(context: Context): Int {
return KeyguardSmartspaceViewModel.getSmartspaceEndMargin(context)
}
- fun getSmallClockSmartspaceTopPadding(splitShadePreview: Boolean): Int {
- return getSmallClockTopPadding(splitShadePreview) +
+ fun getSmallClockSmartspaceTopPadding(splitShadePreview: Boolean, context: Context): Int {
+ return getSmallClockTopPadding(splitShadePreview, context) +
context.resources.getDimensionPixelSize(
com.android.systemui.customization.R.dimen.small_clock_height
)
}
- fun getLargeClockSmartspaceTopPadding(splitShadePreview: Boolean): Int {
- return getSmallClockTopPadding(splitShadePreview)
+ fun getLargeClockSmartspaceTopPadding(splitShadePreview: Boolean, context: Context): Int {
+ return getSmallClockTopPadding(splitShadePreview, context)
}
/*
* SmallClockTopPadding decides the top position of smartspace
*/
- private fun getSmallClockTopPadding(splitShadePreview: Boolean): Int {
+ private fun getSmallClockTopPadding(splitShadePreview: Boolean, context: Context): Int {
return with(context.resources) {
if (splitShadePreview) {
getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
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 64e1565..24a7c51 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
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.util.kotlin.BooleanFlowOperators.or
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.ui.AnimatableEvent
@@ -133,22 +134,18 @@
private val isOnLockscreen: Flow<Boolean> =
combine(
keyguardTransitionInteractor.isFinishedInState(LOCKSCREEN).onStart { emit(false) },
- keyguardTransitionInteractor
- .isInTransitionWhere { from, to -> from == LOCKSCREEN || to == LOCKSCREEN }
- .onStart { emit(false) }
+ or(
+ keyguardTransitionInteractor.isInTransitionToState(LOCKSCREEN),
+ keyguardTransitionInteractor.isInTransitionFromState(LOCKSCREEN),
+ ),
) { onLockscreen, transitioningToOrFromLockscreen ->
onLockscreen || transitioningToOrFromLockscreen
}
.distinctUntilChanged()
- private val lockscreenToGoneTransitionRunning: Flow<Boolean> =
- keyguardTransitionInteractor
- .isInTransitionWhere { from, to -> from == LOCKSCREEN && to == GONE }
- .onStart { emit(false) }
-
private val alphaOnShadeExpansion: Flow<Float> =
combineTransform(
- lockscreenToGoneTransitionRunning,
+ keyguardTransitionInteractor.isInTransition(from = LOCKSCREEN, to = GONE),
isOnLockscreen,
shadeInteractor.qsExpansion,
shadeInteractor.shadeExpansion,
@@ -185,8 +182,12 @@
.transitionValue(OCCLUDED)
.map { it == 1f }
.onStart { emit(false) },
- ) { isIdleOnCommunal, isGone, isOccluded ->
- isIdleOnCommunal || isGone || isOccluded
+ keyguardTransitionInteractor
+ .transitionValue(KeyguardState.DREAMING)
+ .map { it == 1f }
+ .onStart { emit(false) },
+ ) { isIdleOnCommunal, isGone, isOccluded, isDreaming ->
+ isIdleOnCommunal || isGone || isOccluded || isDreaming
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index 36896f9..ecad148 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -27,6 +27,7 @@
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -46,6 +47,7 @@
val longPress: KeyguardLongPressViewModel,
val shadeInteractor: ShadeInteractor,
@Application private val applicationScope: CoroutineScope,
+ private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
) {
private val clockSize = clockInteractor.clockSize
@@ -75,6 +77,23 @@
initialValue = false,
)
+ /** Amount of horizontal translation that should be applied to elements in the scene. */
+ val unfoldTranslations: StateFlow<UnfoldTranslations> =
+ combine(
+ unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = true),
+ unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = false),
+ ) { start, end ->
+ UnfoldTranslations(
+ start = start,
+ end = end,
+ )
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = UnfoldTranslations(),
+ )
+
fun getSmartSpacePaddingTop(resources: Resources): Int {
return if (isLargeClockVisible) {
resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) +
@@ -94,4 +113,20 @@
initialValue = interactor.getCurrentBlueprint().id,
)
}
+
+ data class UnfoldTranslations(
+
+ /**
+ * Amount of horizontal translation to apply to elements that are aligned to the start side
+ * (left in left-to-right layouts). Can also be used as horizontal padding for elements that
+ * need horizontal padding on both side. In pixels.
+ */
+ val start: Float = 0f,
+
+ /**
+ * Amount of horizontal translation to apply to elements that are aligned to the end side
+ * (right in left-to-right layouts). In pixels.
+ */
+ val end: Float = 0f,
+ )
}
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 993e81b..d4c8456e 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
@@ -37,6 +37,8 @@
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
/** Models UI state and handles user input for the lockscreen scene. */
@@ -52,16 +54,23 @@
val notifications: NotificationsPlaceholderViewModel,
) {
val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
- combine(
- deviceEntryInteractor.isUnlocked,
- communalInteractor.isCommunalAvailable,
- shadeInteractor.shadeMode,
- ) { isDeviceUnlocked, isCommunalAvailable, shadeMode ->
- destinationScenes(
- isDeviceUnlocked = isDeviceUnlocked,
- isCommunalAvailable = isCommunalAvailable,
- shadeMode = shadeMode,
- )
+ shadeInteractor.isShadeTouchable
+ .flatMapLatest { isShadeTouchable ->
+ if (!isShadeTouchable) {
+ flowOf(emptyMap())
+ } else {
+ combine(
+ deviceEntryInteractor.isUnlocked,
+ communalInteractor.isCommunalAvailable,
+ shadeInteractor.shadeMode,
+ ) { isDeviceUnlocked, isCommunalAvailable, shadeMode ->
+ destinationScenes(
+ isDeviceUnlocked = isDeviceUnlocked,
+ isCommunalAvailable = isCommunalAvailable,
+ shadeMode = shadeMode,
+ )
+ }
+ }
}
.stateIn(
scope = applicationScope,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
new file mode 100644
index 0000000..d2c9cfb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/** Breaks down OCCLUDED->GONE transition into discrete steps for corresponding views to consume. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class OccludedToGoneTransitionViewModel
+@Inject
+constructor(
+ animationFlow: KeyguardTransitionAnimationFlow,
+) {
+ private val transitionAnimation =
+ animationFlow.setup(
+ duration = DEFAULT_DURATION,
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.GONE,
+ )
+
+ fun notificationAlpha(viewState: ViewStateAccessor): Flow<Float> {
+ var currentAlpha = 0f
+ return transitionAnimation.sharedFlow(
+ duration = DEFAULT_DURATION,
+ onStart = { currentAlpha = viewState.alpha() },
+ onStep = { currentAlpha },
+ onFinish = { 1f },
+ )
+ }
+
+ companion object {
+ val DEFAULT_DURATION = 300.milliseconds
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index 3a19780..a09d58a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -21,15 +21,21 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
/**
* Breaks down OCCLUDED->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -43,6 +49,8 @@
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
+ keyguardInteractor: KeyguardInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
) : DeviceEntryIconTransition {
private val transitionAnimation =
@@ -74,11 +82,28 @@
/** Lockscreen views alpha */
val lockscreenAlpha: Flow<Float> =
- transitionAnimation.sharedFlow(
- startTime = 233.milliseconds,
- duration = 250.milliseconds,
- onStep = { it },
- name = "OCCLUDED->LOCKSCREEN: lockscreenAlpha",
+ merge(
+ transitionAnimation.sharedFlow(
+ startTime = 233.milliseconds,
+ duration = 250.milliseconds,
+ onStep = { it },
+ name = "OCCLUDED->LOCKSCREEN: lockscreenAlpha",
+ ),
+ // Required to fix a bug where the shade expands while lockscreenAlpha=1f, due to a call
+ // to setOccluded(false) triggering a reset() call in KeyguardViewMediator. The
+ // permanent solution is to only expand the shade once the keyguard transition from
+ // OCCLUDED starts, but that requires more refactoring of expansion amounts. For now,
+ // emit alpha = 0f for OCCLUDED -> LOCKSCREEN whenever isOccluded flips from true to
+ // false while currentState == OCCLUDED, so that alpha = 0f when that expansion occurs.
+ // TODO(b/332946323): Remove this once it's no longer needed.
+ keyguardInteractor.isKeyguardOccluded
+ .pairwise()
+ .filter { (wasOccluded, isOccluded) ->
+ wasOccluded &&
+ !isOccluded &&
+ keyguardTransitionInteractor.getCurrentState() == KeyguardState.OCCLUDED
+ }
+ .map { 0f }
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index 0587826..a08a234 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -18,10 +18,9 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
+import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.ScrimAlpha
@@ -46,7 +45,6 @@
private val statusBarStateController: SysuiStatusBarStateController,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
- featureFlags: FeatureFlagsClassic,
bouncerToGoneFlows: BouncerToGoneFlows,
animationFlow: KeyguardTransitionAnimationFlow,
) {
@@ -82,7 +80,7 @@
/** Bouncer container alpha */
val bouncerAlpha: Flow<Float> =
- if (featureFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ if (RefactorKeyguardDismissIntent.isEnabled) {
keyguardDismissActionInteractor
.get()
.willAnimateDismissActionOnLockscreen
@@ -106,7 +104,7 @@
/** Lockscreen alpha */
val lockscreenAlpha: Flow<Float> =
- if (featureFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ if (RefactorKeyguardDismissIntent.isEnabled) {
keyguardDismissActionInteractor
.get()
.willAnimateDismissActionOnLockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/model/MediaSortKeyModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/model/MediaSortKeyModel.kt
new file mode 100644
index 0000000..cfe5cde
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/model/MediaSortKeyModel.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.data.model
+
+import com.android.internal.logging.InstanceId
+import com.android.systemui.media.controls.shared.model.MediaData.Companion.PLAYBACK_LOCAL
+
+data class MediaSortKeyModel(
+ /** Whether the item represents a Smartspace media recommendation that should be prioritized. */
+ val isPrioritizedRec: Boolean = false,
+ val isPlaying: Boolean? = null,
+ val playbackLocation: Int = PLAYBACK_LOCAL,
+ val active: Boolean = true,
+ val isResume: Boolean = false,
+ val lastActive: Long = 0L,
+ val notificationKey: String? = null,
+ val updateTime: Long = 0,
+ val instanceId: InstanceId? = null,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
index df34169..7e57cf4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
@@ -18,8 +18,14 @@
import com.android.internal.logging.InstanceId
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.controls.data.model.MediaSortKeyModel
+import com.android.systemui.media.controls.shared.model.MediaCommonModel
import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
+import com.android.systemui.util.time.SystemClock
+import java.util.TreeMap
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -27,7 +33,7 @@
/** A repository that holds the state of filtered media data on the device. */
@SysUISingleton
-class MediaFilterRepository @Inject constructor() {
+class MediaFilterRepository @Inject constructor(private val systemClock: SystemClock) {
/** Instance id of media control that recommendations card reactivated. */
private val _reactivatedId: MutableStateFlow<InstanceId?> = MutableStateFlow(null)
@@ -46,6 +52,36 @@
MutableStateFlow(LinkedHashMap())
val allUserEntries: StateFlow<Map<String, MediaData>> = _allUserEntries.asStateFlow()
+ private val _mediaDataLoadedStates: MutableStateFlow<List<MediaDataLoadingModel>> =
+ MutableStateFlow(mutableListOf())
+ val mediaDataLoadedStates: StateFlow<List<MediaDataLoadingModel>> =
+ _mediaDataLoadedStates.asStateFlow()
+
+ private val _recommendationsLoadingState: MutableStateFlow<SmartspaceMediaLoadingModel> =
+ MutableStateFlow(SmartspaceMediaLoadingModel.Unknown)
+ val recommendationsLoadingState: StateFlow<SmartspaceMediaLoadingModel> =
+ _recommendationsLoadingState.asStateFlow()
+
+ private val comparator =
+ compareByDescending<MediaSortKeyModel> {
+ it.isPlaying == true && it.playbackLocation == MediaData.PLAYBACK_LOCAL
+ }
+ .thenByDescending {
+ it.isPlaying == true && it.playbackLocation == MediaData.PLAYBACK_CAST_LOCAL
+ }
+ .thenByDescending { it.active }
+ .thenByDescending { it.isPrioritizedRec }
+ .thenByDescending { !it.isResume }
+ .thenByDescending { it.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE }
+ .thenByDescending { it.lastActive }
+ .thenByDescending { it.updateTime }
+ .thenByDescending { it.notificationKey }
+
+ private val _sortedMedia: MutableStateFlow<TreeMap<MediaSortKeyModel, MediaCommonModel>> =
+ MutableStateFlow(TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator))
+ val sortedMedia: StateFlow<Map<MediaSortKeyModel, MediaCommonModel>> =
+ _sortedMedia.asStateFlow()
+
fun addMediaEntry(key: String, data: MediaData) {
val entries = LinkedHashMap<String, MediaData>(_allUserEntries.value)
entries[key] = data
@@ -110,4 +146,107 @@
fun setReactivatedId(instanceId: InstanceId?) {
_reactivatedId.value = instanceId
}
+
+ fun addMediaDataLoadingState(mediaDataLoadingModel: MediaDataLoadingModel) {
+ // Filter out previous loading state that has same [InstanceId].
+ val loadedStates =
+ _mediaDataLoadedStates.value.filter { loadedModel ->
+ loadedModel !is MediaDataLoadingModel.Loaded ||
+ !loadedModel.equalInstanceIds(mediaDataLoadingModel)
+ }
+
+ _mediaDataLoadedStates.value =
+ loadedStates +
+ if (mediaDataLoadingModel is MediaDataLoadingModel.Loaded) {
+ listOf(mediaDataLoadingModel)
+ } else {
+ emptyList()
+ }
+
+ addMediaLoadingToSortedMap(mediaDataLoadingModel)
+ }
+
+ fun setRecommendationsLoadingState(smartspaceMediaLoadingModel: SmartspaceMediaLoadingModel) {
+ _recommendationsLoadingState.value = smartspaceMediaLoadingModel
+
+ addRecsLoadingToSortedMap(smartspaceMediaLoadingModel)
+ }
+
+ private fun addMediaLoadingToSortedMap(mediaDataLoadingModel: MediaDataLoadingModel) {
+ val instanceId =
+ when (mediaDataLoadingModel) {
+ is MediaDataLoadingModel.Loaded -> mediaDataLoadingModel.instanceId
+ is MediaDataLoadingModel.Removed -> mediaDataLoadingModel.instanceId
+ MediaDataLoadingModel.Unknown -> null
+ }
+ val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
+ sortedMap.putAll(
+ _sortedMedia.value.filter { (_, commonModel) ->
+ commonModel !is MediaCommonModel.MediaControl ||
+ commonModel.instanceId != instanceId
+ }
+ )
+
+ _selectedUserEntries.value[instanceId]?.let {
+ val sortKey =
+ MediaSortKeyModel(
+ isPrioritizedRec = false,
+ it.isPlaying,
+ it.playbackLocation,
+ it.active,
+ it.resumption,
+ it.lastActive,
+ it.notificationKey,
+ systemClock.currentTimeMillis(),
+ it.instanceId,
+ )
+
+ if (mediaDataLoadingModel is MediaDataLoadingModel.Loaded) {
+ sortedMap[sortKey] = MediaCommonModel.MediaControl(it.instanceId)
+ }
+ }
+
+ _sortedMedia.value = sortedMap
+ }
+
+ private fun addRecsLoadingToSortedMap(
+ smartspaceMediaLoadingModel: SmartspaceMediaLoadingModel
+ ) {
+ val isPrioritized: Boolean
+ val key: String?
+ when (smartspaceMediaLoadingModel) {
+ is SmartspaceMediaLoadingModel.Loaded -> {
+ isPrioritized = smartspaceMediaLoadingModel.isPrioritized
+ key = smartspaceMediaLoadingModel.key
+ }
+ is SmartspaceMediaLoadingModel.Removed -> {
+ isPrioritized = false
+ key = smartspaceMediaLoadingModel.key
+ }
+ SmartspaceMediaLoadingModel.Unknown -> {
+ isPrioritized = false
+ key = null
+ }
+ }
+ val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
+ sortedMap.putAll(
+ _sortedMedia.value.filter { (_, commonModel) ->
+ commonModel !is MediaCommonModel.MediaRecommendations || commonModel.key != key
+ }
+ )
+
+ key?.let {
+ val sortKey =
+ MediaSortKeyModel(
+ isPrioritizedRec = isPrioritized,
+ isPlaying = false,
+ active = _smartspaceMediaData.value.isActive,
+ )
+ if (smartspaceMediaLoadingModel is SmartspaceMediaLoadingModel.Loaded) {
+ sortedMap[sortKey] = MediaCommonModel.MediaRecommendations(key)
+ }
+ }
+
+ _sortedMedia.value = sortedMap
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
index d40069c..5432a18 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
@@ -28,7 +28,9 @@
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_RESUME
import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.settings.UserTracker
@@ -67,10 +69,11 @@
private val mediaFlags: MediaFlags,
private val mediaFilterRepository: MediaFilterRepository,
) : MediaDataManager.Listener {
- private val _listeners: MutableSet<Listener> = mutableSetOf()
- val listeners: Set<Listener>
+ /** Non-UI listeners to media changes. */
+ private val _listeners: MutableSet<MediaDataProcessor.Listener> = mutableSetOf()
+ val listeners: Set<MediaDataProcessor.Listener>
get() = _listeners.toSet()
- lateinit var mediaDataManager: MediaDataManager
+ lateinit var mediaDataProcessor: MediaDataProcessor
// Ensure the field (and associated reference) isn't removed during optimization.
@KeepForWeakReference
@@ -111,8 +114,12 @@
mediaFilterRepository.addSelectedUserMediaEntry(data)
+ mediaFilterRepository.addMediaDataLoadingState(
+ MediaDataLoadingModel.Loaded(data.instanceId)
+ )
+
// Notify listeners
- listeners.forEach { it.onMediaDataLoaded(data.instanceId) }
+ listeners.forEach { it.onMediaDataLoaded(key, oldKey, data) }
}
override fun onSmartspaceMediaDataLoaded(
@@ -159,7 +166,7 @@
// reactivate.
if (shouldReactivate) {
val lastActiveId = sorted.lastKey() // most recently active id
- // Notify listeners to consider this media active
+ // Update loading state to consider this media active
Log.d(TAG, "reactivating $lastActiveId instead of smartspace")
mediaFilterRepository.setReactivatedId(lastActiveId)
val mediaData = sorted[lastActiveId]!!.copy(active = true)
@@ -168,14 +175,22 @@
mediaData.packageName,
mediaData.instanceId
)
- listeners.forEach {
- it.onMediaDataLoaded(
- lastActiveId,
- receivedSmartspaceCardLatency =
- (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis)
- .toInt(),
- isSsReactivated = true
- )
+ mediaFilterRepository.addMediaDataLoadingState(
+ MediaDataLoadingModel.Loaded(lastActiveId)
+ )
+ listeners.forEach { listener ->
+ getKey(lastActiveId)?.let { lastActiveKey ->
+ listener.onMediaDataLoaded(
+ lastActiveKey,
+ lastActiveKey,
+ mediaData,
+ receivedSmartspaceCardLatency =
+ (systemClock.currentTimeMillis() -
+ data.headphoneConnectionTimeMillis)
+ .toInt(),
+ isSsReactivated = true
+ )
+ }
}
}
} else if (data.isActive) {
@@ -192,15 +207,21 @@
smartspaceMediaData.packageName,
smartspaceMediaData.instanceId
)
- listeners.forEach { it.onSmartspaceMediaDataLoaded(key, shouldPrioritizeMutable) }
+ mediaFilterRepository.setRecommendationsLoadingState(
+ SmartspaceMediaLoadingModel.Loaded(key, shouldPrioritizeMutable)
+ )
+ listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
}
override fun onMediaDataRemoved(key: String) {
mediaFilterRepository.removeMediaEntry(key)?.let { mediaData ->
val instanceId = mediaData.instanceId
mediaFilterRepository.removeSelectedUserMediaEntry(instanceId)?.let {
+ mediaFilterRepository.addMediaDataLoadingState(
+ MediaDataLoadingModel.Removed(instanceId)
+ )
// Only notify listeners if something actually changed
- listeners.forEach { it.onMediaDataRemoved(instanceId) }
+ listeners.forEach { it.onMediaDataRemoved(key) }
}
}
}
@@ -210,10 +231,15 @@
mediaFilterRepository.reactivatedId.value?.let { lastActiveId ->
mediaFilterRepository.setReactivatedId(null)
Log.d(TAG, "expiring reactivated key $lastActiveId")
- // Notify listeners to update with actual active value
+ // Update loading state with actual active value
mediaFilterRepository.selectedUserEntries.value[lastActiveId]?.let {
+ mediaFilterRepository.addMediaDataLoadingState(
+ MediaDataLoadingModel.Loaded(lastActiveId, immediately)
+ )
listeners.forEach { listener ->
- listener.onMediaDataLoaded(lastActiveId, immediately)
+ getKey(lastActiveId)?.let { lastActiveKey ->
+ listener.onMediaDataLoaded(lastActiveKey, lastActiveKey, it, immediately)
+ }
}
}
}
@@ -227,6 +253,9 @@
)
)
}
+ mediaFilterRepository.setRecommendationsLoadingState(
+ SmartspaceMediaLoadingModel.Removed(key, immediately)
+ )
listeners.forEach { it.onSmartspaceMediaDataRemoved(key, immediately) }
}
@@ -238,29 +267,43 @@
// Only remove media when the profile is unavailable.
if (DEBUG) Log.d(TAG, "Removing $key after profile change")
mediaFilterRepository.removeSelectedUserMediaEntry(data.instanceId, data)
- listeners.forEach { listener -> listener.onMediaDataRemoved(data.instanceId) }
+ mediaFilterRepository.addMediaDataLoadingState(
+ MediaDataLoadingModel.Removed(data.instanceId)
+ )
+ listeners.forEach { listener -> listener.onMediaDataRemoved(key) }
}
}
}
@VisibleForTesting
internal fun handleUserSwitched() {
- // If the user changes, remove all current MediaData objects and inform listeners
+ // If the user changes, remove all current MediaData objects.
val listenersCopy = listeners
val keyCopy = mediaFilterRepository.selectedUserEntries.value.keys.toMutableList()
- // Clear the list first, to make sure callbacks from listeners if we have any entries
- // are up to date
+ // Clear the list first and update loading state to remove media from UI.
mediaFilterRepository.clearSelectedUserMedia()
keyCopy.forEach { instanceId ->
if (DEBUG) Log.d(TAG, "Removing $instanceId after user change")
- listenersCopy.forEach { listener -> listener.onMediaDataRemoved(instanceId) }
+ mediaFilterRepository.addMediaDataLoadingState(
+ MediaDataLoadingModel.Removed(instanceId)
+ )
+ getKey(instanceId)?.let {
+ listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it) }
+ }
}
mediaFilterRepository.allUserEntries.value.forEach { (key, data) ->
if (lockscreenUserManager.isCurrentProfile(data.userId)) {
- if (DEBUG) Log.d(TAG, "Re-adding $key after user change")
+ if (DEBUG)
+ Log.d(
+ TAG,
+ "Re-adding $key with instanceId=${data.instanceId} after user change"
+ )
mediaFilterRepository.addSelectedUserMediaEntry(data)
- listenersCopy.forEach { listener -> listener.onMediaDataLoaded(data.instanceId) }
+ mediaFilterRepository.addMediaDataLoadingState(
+ MediaDataLoadingModel.Loaded(data.instanceId)
+ )
+ listenersCopy.forEach { listener -> listener.onMediaDataLoaded(key, null, data) }
}
}
}
@@ -272,7 +315,7 @@
mediaEntries.forEach { (key, data) ->
if (mediaFilterRepository.selectedUserEntries.value.containsKey(data.instanceId)) {
// Force updates to listeners, needed for re-activated card
- mediaDataManager.setInactive(key, timedOut = true, forceUpdate = true)
+ mediaDataProcessor.setInactive(key, timedOut = true, forceUpdate = true)
}
}
val smartspaceMediaData = mediaFilterRepository.smartspaceMediaData.value
@@ -294,7 +337,7 @@
if (mediaFlags.isPersistentSsCardEnabled()) {
mediaFilterRepository.setRecommendation(smartspaceMediaData.copy(isActive = false))
- mediaDataManager.setRecommendationInactive(smartspaceMediaData.targetId)
+ mediaDataProcessor.setRecommendationInactive(smartspaceMediaData.targetId)
} else {
mediaFilterRepository.setRecommendation(
EMPTY_SMARTSPACE_MEDIA_DATA.copy(
@@ -302,7 +345,7 @@
instanceId = smartspaceMediaData.instanceId,
)
)
- mediaDataManager.dismissSmartspaceRecommendation(
+ mediaDataProcessor.dismissSmartspaceRecommendation(
smartspaceMediaData.targetId,
delay = 0L,
)
@@ -311,10 +354,10 @@
}
/** Add a listener for filtered [MediaData] changes */
- fun addListener(listener: Listener) = _listeners.add(listener)
+ fun addListener(listener: MediaDataProcessor.Listener) = _listeners.add(listener)
/** Remove a listener that was registered with addListener */
- fun removeListener(listener: Listener) = _listeners.remove(listener)
+ fun removeListener(listener: MediaDataProcessor.Listener) = _listeners.remove(listener)
/**
* Return the time since last active for the most-recent media.
@@ -335,46 +378,14 @@
return sortedEntries[lastActiveInstanceId]?.let { now - it.lastActive } ?: Long.MAX_VALUE
}
- interface Listener {
- /**
- * Called whenever there's new MediaData Loaded for the consumption in views.
- *
- * @param immediately indicates should apply the UI changes immediately, otherwise wait
- * until the next refresh-round before UI becomes visible. True by default to take in
- * place immediately.
- * @param receivedSmartspaceCardLatency is the latency between headphone connects and sysUI
- * displays Smartspace media targets. Will be 0 if the data is not activated by Smartspace
- * signal.
- * @param isSsReactivated indicates resume media card is reactivated by Smartspace
- * recommendation signal
- */
- fun onMediaDataLoaded(
- instanceId: InstanceId,
- immediately: Boolean = true,
- receivedSmartspaceCardLatency: Int = 0,
- isSsReactivated: Boolean = false,
- )
-
- /**
- * Called whenever there's new Smartspace media data loaded.
- *
- * @param shouldPrioritize indicates the sorting priority of the Smartspace card. If true,
- * it will be prioritized as the first card. Otherwise, it will show up as the last card
- * as default.
- */
- fun onSmartspaceMediaDataLoaded(key: String, shouldPrioritize: Boolean = false)
-
- /** Called whenever a previously existing Media notification was removed. */
- fun onMediaDataRemoved(instanceId: InstanceId)
-
- /**
- * Called whenever a previously existing Smartspace media data was removed.
- *
- * @param immediately indicates should apply the UI changes immediately, otherwise wait
- * until the next refresh-round before UI becomes visible. True by default to take in
- * place immediately.
- */
- fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean = true)
+ private fun getKey(instanceId: InstanceId): String? {
+ val allEntries = mediaFilterRepository.allUserEntries.value
+ val filteredEntries = allEntries.filter { (_, data) -> data.instanceId == instanceId }
+ return if (filteredEntries.isNotEmpty()) {
+ filteredEntries.keys.first()
+ } else {
+ null
+ }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index 7412290..1d7c025 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -590,7 +590,7 @@
}
/** Dismiss a media entry. Returns false if the key was not found. */
- fun dismissMediaData(key: String, delay: Long): Boolean {
+ fun dismissMediaData(key: String, delayMs: Long): Boolean {
val existed = mediaDataRepository.mediaEntries.value[key] != null
backgroundExecutor.execute {
mediaDataRepository.mediaEntries.value[key]?.let { mediaData ->
@@ -602,10 +602,21 @@
}
}
}
- foregroundExecutor.executeDelayed({ removeEntry(key) }, delay)
+ foregroundExecutor.executeDelayed({ removeEntry(key) }, delayMs)
return existed
}
+ /** Dismiss a media entry. Returns false if the corresponding key was not found. */
+ fun dismissMediaData(instanceId: InstanceId, delayMs: Long): Boolean {
+ val mediaEntries = mediaDataRepository.mediaEntries.value
+ val filteredEntries = mediaEntries.filter { (_, data) -> data.instanceId == instanceId }
+ return if (filteredEntries.isNotEmpty()) {
+ dismissMediaData(filteredEntries.keys.first(), delayMs)
+ } else {
+ false
+ }
+ }
+
/**
* Called whenever the recommendation has been expired or removed by the user. This will remove
* the recommendation card entirely from the carousel.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index c7cfb0b..0e2814b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -35,6 +35,7 @@
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.PhoneMediaDevice
+import com.android.settingslib.media.flags.Flags
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.controls.shared.model.MediaData
@@ -178,7 +179,9 @@
bgExecutor.execute {
if (!started) {
localMediaManager.registerCallback(this)
- localMediaManager.startScan()
+ if (!Flags.removeUnnecessaryRouteScanning()) {
+ localMediaManager.startScan()
+ }
muteAwaitConnectionManager.startListening()
playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
playbackVolumeControlId = controller?.playbackInfo?.volumeControlId
@@ -195,7 +198,9 @@
if (started) {
started = false
controller?.unregisterCallback(this)
- localMediaManager.stopScan()
+ if (!Flags.removeUnnecessaryRouteScanning()) {
+ localMediaManager.stopScan()
+ }
localMediaManager.unregisterCallback(this)
muteAwaitConnectionManager.stopListening()
configurationController.removeCallback(configListener)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index 7dbca0a..b04e938 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -34,15 +34,21 @@
import com.android.systemui.media.controls.domain.pipeline.MediaSessionBasedFilter
import com.android.systemui.media.controls.domain.pipeline.MediaTimeoutListener
import com.android.systemui.media.controls.domain.resume.MediaResumeListener
+import com.android.systemui.media.controls.shared.model.MediaCommonModel
+import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
+import com.android.systemui.media.controls.util.MediaControlsRefactorFlag
import com.android.systemui.media.controls.util.MediaFlags
import java.io.PrintWriter
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.distinctUntilChanged
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
@@ -109,6 +115,18 @@
.distinctUntilChanged()
.stateIn(applicationScope, SharingStarted.WhileSubscribed(), false)
+ /** The most recent list of loaded media controls. */
+ val mediaDataLoadedStates: Flow<List<MediaDataLoadingModel>> =
+ mediaFilterRepository.mediaDataLoadedStates
+
+ /** The most recent change to loaded media recommendations. */
+ val recommendationsLoadingState: Flow<SmartspaceMediaLoadingModel> =
+ mediaFilterRepository.recommendationsLoadingState
+
+ /** The most recent sorted set for user media instances */
+ val sortedMedia: Flow<List<MediaCommonModel>> =
+ mediaFilterRepository.sortedMedia.map { it.values.toList() }
+
override fun start() {
if (!mediaFlags.isMediaControlsRefactorEnabled()) {
return
@@ -139,13 +157,19 @@
mediaDataProcessor.onSessionDestroyed(key)
}
mediaResumeListener.setManager(this)
- mediaDataFilter.mediaDataManager = this
+ mediaDataFilter.mediaDataProcessor = mediaDataProcessor
}
- override fun setInactive(key: String, timedOut: Boolean, forceUpdate: Boolean) {
- mediaDataProcessor.setInactive(key, timedOut, forceUpdate)
+ override fun addListener(listener: MediaDataManager.Listener) {
+ mediaDataFilter.addListener(listener)
}
+ override fun removeListener(listener: MediaDataManager.Listener) {
+ mediaDataFilter.removeListener(listener)
+ }
+
+ override fun setInactive(key: String, timedOut: Boolean, forceUpdate: Boolean) = unsupported
+
override fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
mediaDataProcessor.onNotificationAdded(key, sbn)
}
@@ -190,9 +214,7 @@
return mediaDataProcessor.dismissSmartspaceRecommendation(key, delay)
}
- override fun setRecommendationInactive(key: String) {
- mediaDataProcessor.setRecommendationInactive(key)
- }
+ override fun setRecommendationInactive(key: String) = unsupported
override fun onNotificationRemoved(key: String) {
mediaDataProcessor.onNotificationRemoved(key)
@@ -223,4 +245,12 @@
override fun dump(pw: PrintWriter, args: Array<out String>) {
mediaDeviceManager.dump(pw)
}
+
+ companion object {
+ val unsupported: Nothing
+ get() =
+ error(
+ "Code path not supported when ${MediaControlsRefactorFlag.FLAG_NAME} is enabled"
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
index 5a0388d..74cd2fe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
@@ -16,20 +16,49 @@
package com.android.systemui.media.controls.domain.pipeline.interactor
+import android.app.ActivityOptions
+import android.app.BroadcastOptions
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
+import android.provider.Settings
+import android.util.Log
+import com.android.internal.jank.Cuj
import com.android.internal.logging.InstanceId
+import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
+import com.android.systemui.bluetooth.BroadcastDialogController
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
import com.android.systemui.media.controls.shared.model.MediaControlModel
import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.dialog.MediaOutputDialogManager
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.kotlin.pairwiseBy
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
/** Encapsulates business logic for single media control. */
class MediaControlInteractor(
- instanceId: InstanceId,
+ @Application applicationContext: Context,
+ private val instanceId: InstanceId,
repository: MediaFilterRepository,
private val mediaDataProcessor: MediaDataProcessor,
+ private val keyguardStateController: KeyguardStateController,
+ private val activityStarter: ActivityStarter,
+ private val activityIntentHelper: ActivityIntentHelper,
+ private val lockscreenUserManager: NotificationLockscreenUserManager,
+ private val mediaOutputDialogManager: MediaOutputDialogManager,
+ private val broadcastDialogController: BroadcastDialogController,
) {
val mediaControl: Flow<MediaControlModel?> =
@@ -37,8 +66,32 @@
.map { entries -> entries[instanceId]?.let { toMediaControlModel(it) } }
.distinctUntilChanged()
- fun removeMediaControl(key: String, delayMs: Long): Boolean {
- return mediaDataProcessor.dismissMediaData(key, delayMs)
+ val isStartedPlaying: Flow<Boolean> =
+ mediaControl
+ .map { mediaControl ->
+ mediaControl?.token?.let { token ->
+ MediaController(applicationContext, token).playbackState?.let {
+ it.state == PlaybackState.STATE_PLAYING
+ }
+ }
+ ?: false
+ }
+ .pairwiseBy(initialValue = false) { wasPlaying, isPlaying -> !wasPlaying && isPlaying }
+ .distinctUntilChanged()
+
+ fun removeMediaControl(
+ token: MediaSession.Token?,
+ instanceId: InstanceId,
+ delayMs: Long
+ ): Boolean {
+ val dismissed = mediaDataProcessor.dismissMediaData(instanceId, delayMs)
+ if (!dismissed) {
+ Log.w(
+ TAG,
+ "Manager failed to dismiss media of instanceId=$instanceId, Token uid=${token?.uid}"
+ )
+ }
+ return dismissed
}
private fun toMediaControlModel(data: MediaData): MediaControlModel {
@@ -53,14 +106,89 @@
appName = app,
songName = song,
artistName = artist,
+ showExplicit = isExplicit,
artwork = artwork,
deviceData = device,
semanticActionButtons = semanticActions,
notificationActionButtons = actions,
actionsToShowInCollapsed = actionsToShowInCompact,
+ isDismissible = isClearable,
isResume = resumption,
resumeProgress = resumeProgress,
)
}
}
+
+ fun startSettings() {
+ activityStarter.startActivity(SETTINGS_INTENT, /* dismissShade= */ true)
+ }
+
+ fun startClickIntent(expandable: Expandable, clickIntent: PendingIntent) {
+ if (!launchOverLockscreen(clickIntent)) {
+ activityStarter.postStartActivityDismissingKeyguard(
+ clickIntent,
+ expandable.activityTransitionController(Cuj.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER)
+ )
+ }
+ }
+
+ fun startDeviceIntent(deviceIntent: PendingIntent) {
+ if (deviceIntent.isActivity) {
+ if (!launchOverLockscreen(deviceIntent)) {
+ activityStarter.postStartActivityDismissingKeyguard(deviceIntent)
+ }
+ } else {
+ Log.w(TAG, "Device pending intent of instanceId=$instanceId is not an activity.")
+ }
+ }
+
+ private fun launchOverLockscreen(pendingIntent: PendingIntent): Boolean {
+ val showOverLockscreen =
+ keyguardStateController.isShowing &&
+ activityIntentHelper.wouldPendingShowOverLockscreen(
+ pendingIntent,
+ lockscreenUserManager.currentUserId
+ )
+ if (showOverLockscreen) {
+ try {
+ val options = BroadcastOptions.makeBasic()
+ options.isInteractive = true
+ options.pendingIntentBackgroundActivityStartMode =
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ pendingIntent.send(options.toBundle())
+ } catch (e: PendingIntent.CanceledException) {
+ Log.e(TAG, "pending intent of $instanceId was canceled")
+ }
+ return true
+ }
+ return false
+ }
+
+ fun startMediaOutputDialog(expandable: Expandable, packageName: String) {
+ mediaOutputDialogManager.createAndShowWithController(
+ packageName,
+ true,
+ expandable.dialogController()
+ )
+ }
+
+ fun startBroadcastDialog(expandable: Expandable, broadcastApp: String, packageName: String) {
+ broadcastDialogController.createBroadcastDialogWithController(
+ broadcastApp,
+ packageName,
+ expandable.dialogTransitionController()
+ )
+ }
+
+ private fun Expandable.dialogController(): DialogTransitionAnimator.Controller? {
+ return dialogTransitionController(
+ cuj =
+ DialogCuj(Cuj.CUJ_SHADE_DIALOG_OPEN, MediaOutputDialogManager.INTERACTION_JANK_TAG)
+ )
+ }
+
+ companion object {
+ private const val TAG = "MediaControlInteractor"
+ private val SETTINGS_INTENT = Intent(Settings.ACTION_MEDIA_CONTROLS_SETTINGS)
+ }
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
similarity index 65%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
index f6140f5..83e2765 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
@@ -14,9 +14,13 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.media.controls.shared.model
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import com.android.internal.logging.InstanceId
+
+/** Models any type of media. */
+sealed class MediaCommonModel {
+ data class MediaControl(val instanceId: InstanceId) : MediaCommonModel()
+
+ data class MediaRecommendations(val key: String) : MediaCommonModel()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaControlModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaControlModel.kt
index d4e34b5..f9134f5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaControlModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaControlModel.kt
@@ -33,6 +33,7 @@
val appName: String?,
val songName: CharSequence?,
val artistName: CharSequence?,
+ val showExplicit: Boolean,
val artwork: Icon?,
val deviceData: MediaDeviceData?,
/** [MediaButton] contains [MediaAction] objects which represent specific buttons in the UI */
@@ -43,6 +44,7 @@
* [Notification.MediaStyle.setShowActionsInCompactView].
*/
val actionsToShowInCollapsed: List<Int>,
+ val isDismissible: Boolean,
/** Whether player is in resumption state. */
val isResume: Boolean,
/** Track seek bar progress (0 - 1) when [isResume] is true. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt
new file mode 100644
index 0000000..bd42a4df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.shared.model
+
+import com.android.internal.logging.InstanceId
+
+/** Models media data loading state. */
+sealed class MediaDataLoadingModel {
+ /** The initial loading state when no media data has yet loaded. */
+ data object Unknown : MediaDataLoadingModel()
+
+ /** Media data has been loaded. */
+ data class Loaded(
+ val instanceId: InstanceId,
+ val immediatelyUpdateUi: Boolean = true,
+ ) : MediaDataLoadingModel() {
+
+ /** Returns true if [other] has the same instance id, false otherwise. */
+ fun equalInstanceIds(other: MediaDataLoadingModel): Boolean {
+ return when (other) {
+ is Loaded -> other.instanceId == instanceId
+ is Removed -> other.instanceId == instanceId
+ Unknown -> false
+ }
+ }
+ }
+
+ /** Media data has been removed. */
+ data class Removed(
+ val instanceId: InstanceId,
+ ) : MediaDataLoadingModel()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaLoadingModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaLoadingModel.kt
new file mode 100644
index 0000000..6c1e536
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaLoadingModel.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.shared.model
+
+/** Models smartspace media loading state. */
+sealed class SmartspaceMediaLoadingModel {
+ /** The initial loading state when no smartspace media has yet loaded. */
+ data object Unknown : SmartspaceMediaLoadingModel()
+
+ /** Smartspace media has been loaded. */
+ data class Loaded(
+ val key: String,
+ val isPrioritized: Boolean = false,
+ ) : SmartspaceMediaLoadingModel()
+
+ /** Smartspace media has been removed. */
+ data class Removed(
+ val key: String,
+ val immediatelyUpdateUi: Boolean = true,
+ ) : SmartspaceMediaLoadingModel()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
new file mode 100644
index 0000000..14a9179
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.ui.binder
+
+import android.widget.ImageButton
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.res.R
+
+object MediaControlViewBinder {
+
+ fun setVisibleAndAlpha(set: ConstraintSet, resId: Int, visible: Boolean) {
+ setVisibleAndAlpha(set, resId, visible, ConstraintSet.GONE)
+ }
+
+ private fun setVisibleAndAlpha(
+ set: ConstraintSet,
+ resId: Int,
+ visible: Boolean,
+ notVisibleValue: Int
+ ) {
+ set.setVisibility(resId, if (visible) ConstraintSet.VISIBLE else notVisibleValue)
+ set.setAlpha(resId, if (visible) 1.0f else 0.0f)
+ }
+
+ fun updateSeekBarVisibility(constraintSet: ConstraintSet, isSeekBarEnabled: Boolean) {
+ if (isSeekBarEnabled) {
+ constraintSet.setVisibility(R.id.media_progress_bar, ConstraintSet.VISIBLE)
+ constraintSet.setAlpha(R.id.media_progress_bar, 1.0f)
+ } else {
+ constraintSet.setVisibility(R.id.media_progress_bar, ConstraintSet.INVISIBLE)
+ constraintSet.setAlpha(R.id.media_progress_bar, 0.0f)
+ }
+ }
+
+ fun setSemanticButtonVisibleAndAlpha(
+ button: ImageButton,
+ expandedSet: ConstraintSet,
+ collapsedSet: ConstraintSet,
+ visible: Boolean,
+ notVisibleValue: Int,
+ showInCollapsed: Boolean
+ ) {
+ if (notVisibleValue == ConstraintSet.INVISIBLE) {
+ // Since time views should appear instead of buttons.
+ button.isFocusable = visible
+ button.isClickable = visible
+ }
+ setVisibleAndAlpha(expandedSet, button.id, visible, notVisibleValue)
+ setVisibleAndAlpha(collapsedSet, button.id, visible = visible && showInCollapsed)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
new file mode 100644
index 0000000..9c6d59e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.ui.binder
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.content.res.Configuration
+import android.graphics.Matrix
+import android.util.TypedValue
+import android.view.View
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.animation.Expandable
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS
+import com.android.systemui.media.controls.ui.controller.MediaViewController
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.MediaRecViewModel
+import com.android.systemui.media.controls.ui.viewmodel.MediaRecommendationsViewModel
+import com.android.systemui.media.controls.ui.viewmodel.MediaRecsCardViewModel
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
+import com.android.systemui.util.animation.TransitionLayout
+import kotlin.math.min
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+
+object MediaRecommendationsViewBinder {
+
+ /** Binds recommendations view holder to the given view-model */
+ fun bind(
+ viewHolder: RecommendationViewHolder,
+ viewModel: MediaRecommendationsViewModel,
+ mediaViewController: MediaViewController,
+ falsingManager: FalsingManager,
+ ) {
+ mediaViewController.recsConfigurationChangeListener = this::updateRecommendationsVisibility
+ val cardView = viewHolder.recommendations
+ cardView.repeatWhenAttached {
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.mediaRecsCard.collectLatest { viewModel ->
+ viewModel?.let {
+ bindRecsCard(viewHolder, it, mediaViewController, falsingManager)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun bindRecsCard(
+ viewHolder: RecommendationViewHolder,
+ viewModel: MediaRecsCardViewModel,
+ mediaViewController: MediaViewController,
+ falsingManager: FalsingManager,
+ ) {
+ // Bind main card.
+ viewHolder.cardTitle.setTextColor(viewModel.cardTitleColor)
+ viewHolder.recommendations.backgroundTintList = ColorStateList.valueOf(viewModel.cardColor)
+ viewHolder.recommendations.contentDescription =
+ viewModel.contentDescription.invoke(mediaViewController.isGutsVisible)
+
+ viewHolder.recommendations.setOnClickListener {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
+ viewModel.onClicked(Expandable.fromView(it))
+ }
+
+ viewHolder.recommendations.setOnLongClickListener {
+ if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY))
+ return@setOnLongClickListener true
+ if (!mediaViewController.isGutsVisible) {
+ openGuts(viewHolder, viewModel, mediaViewController)
+ } else {
+ closeGuts(viewHolder, viewModel, mediaViewController)
+ }
+ return@setOnLongClickListener true
+ }
+
+ // Bind all recommendations.
+ bindRecommendationsList(viewHolder, viewModel.mediaRecs, falsingManager)
+ updateRecommendationsVisibility(mediaViewController, viewHolder.recommendations)
+
+ // Set visibility of recommendations.
+ val expandedSet: ConstraintSet = mediaViewController.expandedLayout
+ val collapsedSet: ConstraintSet = mediaViewController.collapsedLayout
+ viewHolder.mediaTitles.forEach {
+ setVisibleAndAlpha(expandedSet, it.id, viewModel.areTitlesVisible)
+ setVisibleAndAlpha(collapsedSet, it.id, viewModel.areTitlesVisible)
+ }
+ viewHolder.mediaSubtitles.forEach {
+ setVisibleAndAlpha(expandedSet, it.id, viewModel.areSubtitlesVisible)
+ setVisibleAndAlpha(collapsedSet, it.id, viewModel.areSubtitlesVisible)
+ }
+
+ bindRecommendationsGuts(viewHolder, viewModel, mediaViewController, falsingManager)
+
+ mediaViewController.refreshState()
+ }
+
+ private fun bindRecommendationsGuts(
+ viewHolder: RecommendationViewHolder,
+ viewModel: MediaRecsCardViewModel,
+ mediaViewController: MediaViewController,
+ falsingManager: FalsingManager,
+ ) {
+ val gutsViewHolder = viewHolder.gutsViewHolder
+ val gutsViewModel = viewModel.gutsMenu
+
+ gutsViewHolder.gutsText.text = gutsViewModel.gutsText
+ gutsViewHolder.dismissText.visibility = View.VISIBLE
+ gutsViewHolder.dismiss.isEnabled = true
+ gutsViewHolder.dismiss.setOnClickListener {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
+ closeGuts(viewHolder, viewModel, mediaViewController)
+ gutsViewModel.onDismissClicked.invoke()
+ }
+
+ gutsViewHolder.cancelText.background = gutsViewModel.cancelTextBackground
+ gutsViewHolder.cancel.setOnClickListener {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ closeGuts(viewHolder, viewModel, mediaViewController)
+ }
+ }
+
+ gutsViewHolder.settings.setOnClickListener {
+ if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ gutsViewModel.onSettingsClicked.invoke()
+ }
+ }
+
+ gutsViewHolder.setDismissible(gutsViewModel.isDismissEnabled)
+ gutsViewHolder.setTextPrimaryColor(gutsViewModel.textPrimaryColor)
+ gutsViewHolder.setAccentPrimaryColor(gutsViewModel.accentPrimaryColor)
+ gutsViewHolder.setSurfaceColor(gutsViewModel.surfaceColor)
+ }
+
+ private fun bindRecommendationsList(
+ viewHolder: RecommendationViewHolder,
+ mediaRecs: List<MediaRecViewModel>,
+ falsingManager: FalsingManager
+ ) {
+ mediaRecs.forEachIndexed { index, mediaRecViewModel ->
+ if (index >= NUM_REQUIRED_RECOMMENDATIONS) return@forEachIndexed
+
+ val appIconView = viewHolder.mediaAppIcons[index]
+ appIconView.clearColorFilter()
+ if (mediaRecViewModel.appIcon != null) {
+ appIconView.setImageDrawable(mediaRecViewModel.appIcon)
+ } else {
+ appIconView.setImageResource(R.drawable.ic_music_note)
+ }
+
+ val mediaCoverContainer = viewHolder.mediaCoverContainers[index]
+ mediaCoverContainer.setOnClickListener {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
+ mediaRecViewModel.onClicked.invoke(Expandable.fromView(it), index)
+ }
+ mediaCoverContainer.setOnLongClickListener {
+ if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY))
+ return@setOnLongClickListener true
+ (it.parent as View).performLongClick()
+ return@setOnLongClickListener true
+ }
+
+ val mediaCover = viewHolder.mediaCoverItems[index]
+ val width: Int =
+ mediaCover.context.resources.getDimensionPixelSize(R.dimen.qs_media_rec_album_width)
+ val height: Int =
+ mediaCover.context.resources.getDimensionPixelSize(
+ R.dimen.qs_media_rec_album_height_expanded
+ )
+ val coverMatrix = Matrix(mediaCover.imageMatrix)
+ coverMatrix.postScale(1.25f, 1.25f, 0.5f * width, 0.5f * height)
+ mediaCover.imageMatrix = coverMatrix
+ mediaCover.setImageDrawable(mediaRecViewModel.albumIcon)
+ mediaCover.contentDescription = mediaRecViewModel.contentDescription
+
+ val title = viewHolder.mediaTitles[index]
+ title.text = mediaRecViewModel.title
+ title.setTextColor(ColorStateList.valueOf(mediaRecViewModel.titleColor))
+
+ val subtitle = viewHolder.mediaSubtitles[index]
+ subtitle.text = mediaRecViewModel.subtitle
+ subtitle.setTextColor(ColorStateList.valueOf(mediaRecViewModel.subtitleColor))
+
+ val progressBar = viewHolder.mediaProgressBars[index]
+ progressBar.progress = mediaRecViewModel.progress
+ progressBar.progressTintList = ColorStateList.valueOf(mediaRecViewModel.progressColor)
+ if (mediaRecViewModel.progress == 0) {
+ progressBar.visibility = View.GONE
+ }
+ }
+ }
+
+ private fun openGuts(
+ viewHolder: RecommendationViewHolder,
+ viewModel: MediaRecsCardViewModel,
+ mediaViewController: MediaViewController,
+ ) {
+ viewHolder.marquee(true, MediaViewController.GUTS_ANIMATION_DURATION)
+ mediaViewController.openGuts()
+ viewHolder.recommendations.contentDescription = viewModel.contentDescription.invoke(true)
+ viewModel.onLongClicked.invoke()
+ }
+
+ private fun closeGuts(
+ viewHolder: RecommendationViewHolder,
+ mediaRecsCardViewModel: MediaRecsCardViewModel,
+ mediaViewController: MediaViewController,
+ ) {
+ viewHolder.marquee(false, MediaViewController.GUTS_ANIMATION_DURATION)
+ mediaViewController.closeGuts(false)
+ viewHolder.recommendations.contentDescription =
+ mediaRecsCardViewModel.contentDescription.invoke(false)
+ }
+
+ private fun setVisibleAndAlpha(set: ConstraintSet, resId: Int, visible: Boolean) {
+ set.setVisibility(resId, if (visible) ConstraintSet.VISIBLE else ConstraintSet.GONE)
+ set.setAlpha(resId, if (visible) 1.0f else 0.0f)
+ }
+
+ private fun updateRecommendationsVisibility(
+ mediaViewController: MediaViewController,
+ cardView: TransitionLayout,
+ ) {
+ val fittedRecsNum = getNumberOfFittedRecommendations(cardView.context)
+ val expandedSet = mediaViewController.expandedLayout
+ val collapsedSet = mediaViewController.collapsedLayout
+ val mediaCoverContainers = getMediaCoverContainers(cardView)
+ // Hide media cover that cannot fit in the recommendation card.
+ mediaCoverContainers.forEachIndexed { index, container ->
+ setVisibleAndAlpha(expandedSet, container.id, index < fittedRecsNum)
+ setVisibleAndAlpha(collapsedSet, container.id, index < fittedRecsNum)
+ }
+ }
+
+ private fun getMediaCoverContainers(cardView: TransitionLayout): List<ViewGroup> {
+ return listOf<ViewGroup>(
+ cardView.requireViewById(R.id.media_cover1_container),
+ cardView.requireViewById(R.id.media_cover2_container),
+ cardView.requireViewById(R.id.media_cover3_container),
+ )
+ }
+
+ private fun getNumberOfFittedRecommendations(context: Context): Int {
+ val res = context.resources
+ val config = res.configuration
+ val defaultDpWidth = res.getInteger(R.integer.default_qs_media_rec_width_dp)
+ val recCoverWidth =
+ (res.getDimensionPixelSize(R.dimen.qs_media_rec_album_width) +
+ res.getDimensionPixelSize(R.dimen.qs_media_info_spacing) * 2)
+
+ // On landscape, media controls should take half of the screen width.
+ val displayAvailableDpWidth =
+ if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ config.screenWidthDp / 2
+ } else {
+ config.screenWidthDp
+ }
+ val fittedNum =
+ if (displayAvailableDpWidth > defaultDpWidth) {
+ val recCoverDefaultWidth =
+ res.getDimensionPixelSize(R.dimen.qs_media_rec_default_width)
+ recCoverDefaultWidth / recCoverWidth
+ } else {
+ val displayAvailableWidth =
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ displayAvailableDpWidth.toFloat(),
+ res.displayMetrics
+ )
+ .toInt()
+ displayAvailableWidth / recCoverWidth
+ }
+ return min(fittedNum.toDouble(), NUM_REQUIRED_RECOMMENDATIONS.toDouble()).toInt()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
index 963c602..c02ce3b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
@@ -297,6 +297,7 @@
}
}
- private val activeContainer: ViewGroup? =
- if (useSplitShade) splitShadeContainer else singlePaneContainer
+ // This field is only used to log current active container.
+ private val activeContainer: ViewGroup?
+ get() = if (useSplitShade) splitShadeContainer else singlePaneContainer
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index c3c1e83..d15d45a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -46,6 +46,8 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
@@ -600,7 +602,8 @@
@VisibleForTesting
internal fun listenForAnyStateToGoneKeyguardTransition(scope: CoroutineScope): Job {
return scope.launch {
- keyguardTransitionInteractor.anyStateToGoneTransition
+ keyguardTransitionInteractor
+ .transition(to = GONE)
.filter { it.transitionState == TransitionState.FINISHED }
.collect {
showMediaCarousel()
@@ -612,7 +615,8 @@
@VisibleForTesting
internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job {
return scope.launch {
- keyguardTransitionInteractor.anyStateToLockscreenTransition
+ keyguardTransitionInteractor
+ .transition(to = LOCKSCREEN)
.filter { it.transitionState == TransitionState.FINISHED }
.collect {
if (!allowMediaPlayerOnLockScreen) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 899b9ed..bd3893b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -118,8 +118,9 @@
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.surfaceeffects.PaintDrawCallback;
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect;
-import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState;
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.AnimationState;
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView;
import com.android.systemui.surfaceeffects.ripple.MultiRippleController;
import com.android.systemui.surfaceeffects.ripple.MultiRippleView;
@@ -264,15 +265,15 @@
private boolean mWasPlaying = false;
private boolean mButtonClicked = false;
- private final LoadingEffect.Companion.PaintDrawCallback mNoiseDrawCallback =
- new LoadingEffect.Companion.PaintDrawCallback() {
+ private final PaintDrawCallback mNoiseDrawCallback =
+ new PaintDrawCallback() {
@Override
- public void onDraw(@NonNull Paint loadingPaint) {
- mMediaViewHolder.getLoadingEffectView().draw(loadingPaint);
+ public void onDraw(@NonNull Paint paint) {
+ mMediaViewHolder.getLoadingEffectView().draw(paint);
}
};
- private final LoadingEffect.Companion.AnimationStateChangedCallback mStateChangedCallback =
- new LoadingEffect.Companion.AnimationStateChangedCallback() {
+ private final LoadingEffect.AnimationStateChangedCallback mStateChangedCallback =
+ new LoadingEffect.AnimationStateChangedCallback() {
@Override
public void onStateChanged(@NonNull AnimationState oldState,
@NonNull AnimationState newState) {
@@ -737,8 +738,11 @@
mPackageName, mMediaViewHolder.getSeamlessButton());
} else {
mLogger.logOpenOutputSwitcher(mUid, mPackageName, mInstanceId);
- mMediaOutputDialogManager.createAndShow(mPackageName, true,
- mMediaViewHolder.getSeamlessButton());
+ // TODO: b/321969740 - Populate the userHandle parameter. The user
+ // handle is necessary to disambiguate the same package running on
+ // different users.
+ mMediaOutputDialogManager.createAndShow(
+ mPackageName, true, mMediaViewHolder.getSeamlessButton(), null);
}
} else {
mLogger.logOpenOutputSwitcher(mUid, mPackageName, mInstanceId);
@@ -747,22 +751,30 @@
boolean showOverLockscreen = mKeyguardStateController.isShowing()
&& mActivityIntentHelper.wouldPendingShowOverLockscreen(
deviceIntent, mLockscreenUserManager.getCurrentUserId());
- if (deviceIntent.isActivity() && !showOverLockscreen) {
- mActivityStarter.postStartActivityDismissingKeyguard(deviceIntent);
- } else {
- try {
- BroadcastOptions options = BroadcastOptions.makeBasic();
- options.setInteractive(true);
- options.setPendingIntentBackgroundActivityStartMode(
+ if (deviceIntent.isActivity()) {
+ if (!showOverLockscreen) {
+ mActivityStarter.postStartActivityDismissingKeyguard(
+ deviceIntent);
+ } else {
+ try {
+ BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setInteractive(true);
+ options.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- deviceIntent.send(options.toBundle());
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Device pending intent was canceled");
+ deviceIntent.send(options.toBundle());
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Device pending intent was canceled");
+ }
}
+ } else {
+ Log.w(TAG, "Device pending intent is not an activity.");
}
} else {
- mMediaOutputDialogManager.createAndShow(mPackageName, true,
- mMediaViewHolder.getSeamlessButton());
+ // TODO: b/321969740 - Populate the userHandle parameter. The user
+ // handle is necessary to disambiguate the same package running on
+ // different users.
+ mMediaOutputDialogManager.createAndShow(
+ mPackageName, true, mMediaViewHolder.getSeamlessButton(), null);
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index ad7990b..7fced5f8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -16,41 +16,73 @@
package com.android.systemui.media.controls.ui.controller
+import android.animation.Animator
+import android.animation.AnimatorInflater
+import android.animation.AnimatorSet
import android.content.Context
import android.content.res.Configuration
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.drawable.Drawable
+import android.provider.Settings
+import android.view.View
+import android.view.animation.Interpolator
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
+import com.android.app.animation.Interpolators
import com.android.app.tracing.traceSection
+import com.android.systemui.Flags
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.controls.ui.animation.ColorSchemeTransition
+import com.android.systemui.media.controls.ui.animation.MetadataAnimationHandler
+import com.android.systemui.media.controls.ui.binder.MediaControlViewBinder
+import com.android.systemui.media.controls.ui.binder.SeekBarObserver
import com.android.systemui.media.controls.ui.controller.MediaCarouselController.Companion.calculateAlpha
import com.android.systemui.media.controls.ui.view.GutsViewHolder
import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.surfaceeffects.PaintDrawCallback
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
+import com.android.systemui.surfaceeffects.ripple.MultiRippleController
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
import com.android.systemui.util.animation.MeasurementInput
import com.android.systemui.util.animation.MeasurementOutput
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.animation.TransitionLayoutController
import com.android.systemui.util.animation.TransitionViewState
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.settings.GlobalSettings
import java.lang.Float.max
import java.lang.Float.min
+import java.util.Random
import javax.inject.Inject
/**
* A class responsible for controlling a single instance of a media player handling interactions
* with the view instance and keeping the media view states up to date.
*/
-class MediaViewController
+open class MediaViewController
@Inject
constructor(
private val context: Context,
private val configurationController: ConfigurationController,
private val mediaHostStatesManager: MediaHostStatesManager,
private val logger: MediaViewLogger,
+ private val seekBarViewModel: SeekBarViewModel,
+ @Main private val mainExecutor: DelayableExecutor,
private val mediaFlags: MediaFlags,
+ private val globalSettings: GlobalSettings,
) {
/**
@@ -69,6 +101,7 @@
/** A listener when the current dimensions of the player change */
lateinit var sizeChangedListener: () -> Unit
lateinit var configurationChangeListener: () -> Unit
+ lateinit var recsConfigurationChangeListener: (MediaViewController, TransitionLayout) -> Unit
private var firstRefresh: Boolean = true
@VisibleForTesting private var transitionLayout: TransitionLayout? = null
private val layoutController = TransitionLayoutController()
@@ -130,6 +163,72 @@
return transitionLayout?.translationY ?: 0.0f
}
+ /** Whether artwork is bound. */
+ var isArtworkBound: Boolean = false
+
+ /** previous background artwork */
+ var prevArtwork: Drawable? = null
+
+ /** Whether scrubbing time can show */
+ var canShowScrubbingTime: Boolean = false
+
+ /** Whether user is touching the seek bar to change the position */
+ var isScrubbing: Boolean = false
+
+ var isSeekBarEnabled: Boolean = false
+
+ /** Not visible value for previous button when scrubbing */
+ private var prevNotVisibleValue = ConstraintSet.GONE
+ private var isPrevButtonAvailable = false
+
+ /** Not visible value for next button when scrubbing */
+ private var nextNotVisibleValue = ConstraintSet.GONE
+ private var isNextButtonAvailable = false
+
+ private lateinit var mediaViewHolder: MediaViewHolder
+ private lateinit var seekBarObserver: SeekBarObserver
+ private lateinit var turbulenceNoiseController: TurbulenceNoiseController
+ private lateinit var loadingEffect: LoadingEffect
+ private lateinit var turbulenceNoiseAnimationConfig: TurbulenceNoiseAnimationConfig
+ private lateinit var noiseDrawCallback: PaintDrawCallback
+ private lateinit var stateChangedCallback: LoadingEffect.AnimationStateChangedCallback
+ internal lateinit var metadataAnimationHandler: MetadataAnimationHandler
+ internal lateinit var colorSchemeTransition: ColorSchemeTransition
+ internal lateinit var multiRippleController: MultiRippleController
+
+ private val scrubbingChangeListener =
+ object : SeekBarViewModel.ScrubbingChangeListener {
+ override fun onScrubbingChanged(scrubbing: Boolean) {
+ if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+ if (isScrubbing == scrubbing) return
+ isScrubbing = scrubbing
+ updateDisplayForScrubbingChange()
+ }
+ }
+
+ private val enabledChangeListener =
+ object : SeekBarViewModel.EnabledChangeListener {
+ override fun onEnabledChanged(enabled: Boolean) {
+ if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+ if (isSeekBarEnabled == enabled) return
+ isSeekBarEnabled = enabled
+ MediaControlViewBinder.updateSeekBarVisibility(expandedLayout, isSeekBarEnabled)
+ }
+ }
+
+ /**
+ * Sets the listening state of the player.
+ *
+ * Should be set to true when the QS panel is open. Otherwise, false. This is a signal to avoid
+ * unnecessary work when the QS panel is closed.
+ *
+ * @param listening True when player should be active. Otherwise, false.
+ */
+ fun setListening(listening: Boolean) {
+ if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+ seekBarViewModel.listening = listening
+ }
+
/** A callback for config changes */
private val configurationListener =
object : ConfigurationController.ConfigurationListener {
@@ -160,7 +259,17 @@
)
)
}
- if (this@MediaViewController::configurationChangeListener.isInitialized) {
+ if (mediaFlags.isMediaControlsRefactorEnabled()) {
+ if (
+ this@MediaViewController::recsConfigurationChangeListener.isInitialized
+ ) {
+ transitionLayout?.let {
+ recsConfigurationChangeListener.invoke(this@MediaViewController, it)
+ }
+ }
+ } else if (
+ this@MediaViewController::configurationChangeListener.isInitialized
+ ) {
configurationChangeListener.invoke()
refreshState()
}
@@ -221,6 +330,14 @@
* Notify this controller that the view has been removed and all listeners should be destroyed
*/
fun onDestroy() {
+ if (mediaFlags.isMediaControlsRefactorEnabled()) {
+ if (this::seekBarObserver.isInitialized) {
+ seekBarViewModel.progress.removeObserver(seekBarObserver)
+ }
+ seekBarViewModel.removeScrubbingChangeListener(scrubbingChangeListener)
+ seekBarViewModel.removeEnabledChangeListener(enabledChangeListener)
+ seekBarViewModel.onDestroy()
+ }
mediaHostStatesManager.removeController(this)
configurationController.removeCallback(configurationListener)
}
@@ -535,6 +652,178 @@
)
}
+ fun attachPlayer(mediaViewHolder: MediaViewHolder) {
+ if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+ this.mediaViewHolder = mediaViewHolder
+
+ // Setting up seek bar.
+ seekBarObserver = SeekBarObserver(mediaViewHolder)
+ seekBarViewModel.progress.observeForever(seekBarObserver)
+ seekBarViewModel.attachTouchHandlers(mediaViewHolder.seekBar)
+ seekBarViewModel.setScrubbingChangeListener(scrubbingChangeListener)
+ seekBarViewModel.setEnabledChangeListener(enabledChangeListener)
+
+ val mediaCard = mediaViewHolder.player
+ attach(mediaViewHolder.player, TYPE.PLAYER)
+
+ val turbulenceNoiseView = mediaViewHolder.turbulenceNoiseView
+ turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView)
+
+ multiRippleController = MultiRippleController(mediaViewHolder.multiRippleView)
+
+ // Metadata Animation
+ val titleText = mediaViewHolder.titleText
+ val artistText = mediaViewHolder.artistText
+ val explicitIndicator = mediaViewHolder.explicitIndicator
+ val enter =
+ loadAnimator(
+ mediaCard.context,
+ R.anim.media_metadata_enter,
+ Interpolators.EMPHASIZED_DECELERATE,
+ titleText,
+ artistText,
+ explicitIndicator
+ )
+ val exit =
+ loadAnimator(
+ mediaCard.context,
+ R.anim.media_metadata_exit,
+ Interpolators.EMPHASIZED_ACCELERATE,
+ titleText,
+ artistText,
+ explicitIndicator
+ )
+ metadataAnimationHandler = MetadataAnimationHandler(exit, enter)
+
+ colorSchemeTransition =
+ ColorSchemeTransition(
+ mediaCard.context,
+ mediaViewHolder,
+ multiRippleController,
+ turbulenceNoiseController
+ )
+
+ // For Turbulence noise.
+ val loadingEffectView = mediaViewHolder.loadingEffectView
+ turbulenceNoiseAnimationConfig =
+ createTurbulenceNoiseConfig(
+ loadingEffectView,
+ turbulenceNoiseView,
+ colorSchemeTransition
+ )
+ noiseDrawCallback =
+ object : PaintDrawCallback {
+ override fun onDraw(paint: Paint) {
+ loadingEffectView.draw(paint)
+ }
+ }
+ stateChangedCallback =
+ object : LoadingEffect.AnimationStateChangedCallback {
+ override fun onStateChanged(
+ oldState: LoadingEffect.AnimationState,
+ newState: LoadingEffect.AnimationState
+ ) {
+ if (newState === LoadingEffect.AnimationState.NOT_PLAYING) {
+ loadingEffectView.visibility = View.INVISIBLE
+ } else {
+ loadingEffectView.visibility = View.VISIBLE
+ }
+ }
+ }
+ }
+
+ fun updateAnimatorDurationScale() {
+ if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+ if (this::seekBarObserver.isInitialized) {
+ seekBarObserver.animationEnabled =
+ globalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f) > 0f
+ }
+ }
+
+ /** update view with the needed UI changes when user touches seekbar. */
+ private fun updateDisplayForScrubbingChange() {
+ mainExecutor.execute {
+ val isTimeVisible = canShowScrubbingTime && isScrubbing
+ MediaControlViewBinder.setVisibleAndAlpha(
+ expandedLayout,
+ mediaViewHolder.scrubbingTotalTimeView.id,
+ isTimeVisible
+ )
+ MediaControlViewBinder.setVisibleAndAlpha(
+ expandedLayout,
+ mediaViewHolder.scrubbingElapsedTimeView.id,
+ isTimeVisible
+ )
+
+ MediaControlViewModel.SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.forEach { id ->
+ val isButtonVisible: Boolean
+ val notVisibleValue: Int
+ when (id) {
+ R.id.actionPrev -> {
+ isButtonVisible = isPrevButtonAvailable && !isTimeVisible
+ notVisibleValue = prevNotVisibleValue
+ }
+ R.id.actionNext -> {
+ isButtonVisible = isNextButtonAvailable && !isTimeVisible
+ notVisibleValue = nextNotVisibleValue
+ }
+ else -> {
+ isButtonVisible = !isTimeVisible
+ notVisibleValue = ConstraintSet.GONE
+ }
+ }
+ MediaControlViewBinder.setSemanticButtonVisibleAndAlpha(
+ mediaViewHolder.getAction(id),
+ expandedLayout,
+ collapsedLayout,
+ isButtonVisible,
+ notVisibleValue,
+ showInCollapsed = true
+ )
+ }
+
+ if (!metadataAnimationHandler.isRunning) {
+ refreshState()
+ }
+ }
+ }
+
+ fun bindSeekBar(onSeek: () -> Unit, onBindSeekBar: (SeekBarViewModel) -> Unit) {
+ if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+ seekBarViewModel.logSeek = onSeek
+ onBindSeekBar.invoke(seekBarViewModel)
+ }
+
+ fun setUpTurbulenceNoise() {
+ if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+ if (Flags.shaderlibLoadingEffectRefactor()) {
+ if (!this::loadingEffect.isInitialized) {
+ loadingEffect =
+ LoadingEffect(
+ TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
+ turbulenceNoiseAnimationConfig,
+ noiseDrawCallback,
+ stateChangedCallback
+ )
+ }
+ colorSchemeTransition.loadingEffect = loadingEffect
+ loadingEffect.play()
+ mainExecutor.executeDelayed(
+ loadingEffect::finish,
+ MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION
+ )
+ } else {
+ turbulenceNoiseController.play(
+ TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
+ turbulenceNoiseAnimationConfig
+ )
+ mainExecutor.executeDelayed(
+ turbulenceNoiseController::finish,
+ MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION
+ )
+ }
+ }
+
/**
* Obtain a measurement for a given location. This makes sure that the state is up to date and
* all widgets know their location. Calling this method may create a measurement if we don't
@@ -790,6 +1079,75 @@
applyImmediately = true
)
}
+
+ @VisibleForTesting
+ protected open fun loadAnimator(
+ context: Context,
+ animId: Int,
+ motionInterpolator: Interpolator?,
+ vararg targets: View?
+ ): AnimatorSet {
+ val animators = ArrayList<Animator>()
+ for (target in targets) {
+ val animator = AnimatorInflater.loadAnimator(context, animId) as AnimatorSet
+ animator.childAnimations[0].interpolator = motionInterpolator
+ animator.setTarget(target)
+ animators.add(animator)
+ }
+ val result = AnimatorSet()
+ result.playTogether(animators)
+ return result
+ }
+
+ private fun createTurbulenceNoiseConfig(
+ loadingEffectView: LoadingEffectView,
+ turbulenceNoiseView: TurbulenceNoiseView,
+ colorSchemeTransition: ColorSchemeTransition
+ ): TurbulenceNoiseAnimationConfig {
+ val targetView: View =
+ if (Flags.shaderlibLoadingEffectRefactor()) {
+ loadingEffectView
+ } else {
+ turbulenceNoiseView
+ }
+ val width = targetView.width
+ val height = targetView.height
+ val random = Random()
+ return TurbulenceNoiseAnimationConfig(
+ gridCount = 2.14f,
+ TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER,
+ random.nextFloat(),
+ random.nextFloat(),
+ random.nextFloat(),
+ noiseMoveSpeedX = 0.42f,
+ noiseMoveSpeedY = 0f,
+ TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z,
+ // Color will be correctly updated in ColorSchemeTransition.
+ colorSchemeTransition.accentPrimary.currentColor,
+ screenColor = Color.BLACK,
+ width.toFloat(),
+ height.toFloat(),
+ TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS,
+ easeInDuration = 1350f,
+ easeOutDuration = 1350f,
+ targetView.context.resources.displayMetrics.density,
+ lumaMatteBlendFactor = 0.26f,
+ lumaMatteOverallBrightness = 0.09f,
+ shouldInverseNoiseLuminosity = false
+ )
+ }
+
+ fun setUpPrevButtonInfo(isAvailable: Boolean, notVisibleValue: Int = ConstraintSet.GONE) {
+ if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+ isPrevButtonAvailable = isAvailable
+ prevNotVisibleValue = notVisibleValue
+ }
+
+ fun setUpNextButtonInfo(isAvailable: Boolean, notVisibleValue: Int = ConstraintSet.GONE) {
+ if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+ isNextButtonAvailable = isAvailable
+ nextNotVisibleValue = notVisibleValue
+ }
}
/** An internal key for the cache of mediaViewStates. This is a subset of the full host state. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaArtworkHelper.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaArtworkHelper.kt
index eec43a6..3b09f41 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaArtworkHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaArtworkHelper.kt
@@ -18,6 +18,7 @@
import android.app.WallpaperColors
import android.content.Context
+import android.content.pm.PackageManager
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
@@ -27,6 +28,7 @@
import com.android.systemui.media.controls.ui.animation.backgroundEndFromScheme
import com.android.systemui.media.controls.ui.animation.backgroundStartFromScheme
import com.android.systemui.monet.ColorScheme
+import com.android.systemui.monet.Style
import com.android.systemui.util.getColorWithAlpha
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@@ -94,4 +96,21 @@
)
return LayerDrawable(arrayOf(albumArt, gradient))
}
+
+ /** Returns [ColorScheme] of media app given its [packageName]. */
+ fun getColorScheme(
+ applicationContext: Context,
+ packageName: String,
+ tag: String,
+ style: Style = Style.TONAL_SPOT
+ ): ColorScheme? {
+ return try {
+ // Set up media source app's logo.
+ val icon = applicationContext.packageManager.getApplicationIcon(packageName)
+ ColorScheme(WallpaperColors.fromDrawable(icon), darkTheme = true, style)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(tag, "Fail to get media app info", e)
+ null
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/GutsViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/GutsViewModel.kt
index e508e1b..6c7c31c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/GutsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/GutsViewModel.kt
@@ -22,9 +22,9 @@
/** Models UI state for media guts menu */
data class GutsViewModel(
val gutsText: CharSequence,
- @ColorInt val textColor: Int,
- @ColorInt val buttonBackgroundColor: Int,
- @ColorInt val buttonTextColor: Int,
+ @ColorInt val textPrimaryColor: Int,
+ @ColorInt val accentPrimaryColor: Int,
+ @ColorInt val surfaceColor: Int,
val isDismissEnabled: Boolean = true,
val onDismissClicked: () -> Unit,
val cancelTextBackground: Drawable?,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt
new file mode 100644
index 0000000..82099e6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.ui.viewmodel
+
+import android.graphics.drawable.Drawable
+import androidx.constraintlayout.widget.ConstraintSet
+
+/** Models UI state of media buttons in media control. */
+data class MediaActionViewModel(
+ val icon: Drawable?,
+ val contentDescription: CharSequence?,
+ val background: Drawable?,
+ /** whether action is visible if user is touching seekbar to change position. */
+ val isVisibleWhenScrubbing: Boolean = true,
+ val notVisibleValue: Int = ConstraintSet.GONE,
+ val showInCollapsed: Boolean,
+ val rebindId: Int? = null,
+ val buttonId: Int? = null,
+ val isEnabled: Boolean,
+ val onClicked: (Int) -> Unit,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
new file mode 100644
index 0000000..d74506d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.ui.viewmodel
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.media.session.MediaController
+import android.media.session.MediaSession.Token
+import android.text.TextUtils
+import android.util.Log
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.internal.logging.InstanceId
+import com.android.settingslib.flags.Flags.legacyLeAudioSharing
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.media.controls.domain.pipeline.interactor.MediaControlInteractor
+import com.android.systemui.media.controls.shared.model.MediaAction
+import com.android.systemui.media.controls.shared.model.MediaButton
+import com.android.systemui.media.controls.shared.model.MediaControlModel
+import com.android.systemui.media.controls.ui.animation.accentPrimaryFromScheme
+import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
+import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
+import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
+import com.android.systemui.media.controls.util.MediaUiEventLogger
+import com.android.systemui.monet.ColorScheme
+import com.android.systemui.monet.Style
+import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.sample
+import java.util.concurrent.Executor
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+
+/** Models UI state and handles user input for a media control. */
+class MediaControlViewModel(
+ @Application private val applicationContext: Context,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ @Background private val backgroundExecutor: Executor,
+ private val interactor: MediaControlInteractor,
+ private val logger: MediaUiEventLogger,
+) {
+
+ private val isAnyButtonClicked: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ private val playTurbulenceNoise: Flow<Boolean> =
+ interactor.mediaControl.sample(
+ combine(isAnyButtonClicked, interactor.isStartedPlaying) {
+ isButtonClicked,
+ isStartedPlaying ->
+ isButtonClicked && isStartedPlaying
+ }
+ .distinctUntilChanged()
+ )
+
+ val player: Flow<MediaPlayerViewModel?> =
+ combine(playTurbulenceNoise, interactor.mediaControl) { playTurbulenceNoise, mediaControl ->
+ mediaControl?.let { toViewModel(it, playTurbulenceNoise) }
+ }
+ .distinctUntilChanged()
+ .flowOn(backgroundDispatcher)
+
+ private fun onDismissMediaData(
+ token: Token?,
+ uid: Int,
+ packageName: String,
+ instanceId: InstanceId
+ ) {
+ logger.logLongPressDismiss(uid, packageName, instanceId)
+ interactor.removeMediaControl(token, instanceId, MEDIA_PLAYER_ANIMATION_DELAY)
+ }
+
+ private suspend fun toViewModel(
+ model: MediaControlModel,
+ playTurbulenceNoise: Boolean
+ ): MediaPlayerViewModel? {
+ val wallpaperColors =
+ MediaArtworkHelper.getWallpaperColor(
+ applicationContext,
+ backgroundDispatcher,
+ model.artwork,
+ TAG
+ )
+ val scheme =
+ wallpaperColors?.let { ColorScheme(it, true, Style.CONTENT) }
+ ?: MediaArtworkHelper.getColorScheme(
+ applicationContext,
+ model.packageName,
+ TAG,
+ Style.CONTENT
+ )
+ ?: return null
+
+ val gutsViewModel = toGutsViewModel(model, scheme)
+
+ // Resetting button clicks state.
+ isAnyButtonClicked.value = false
+
+ return MediaPlayerViewModel(
+ contentDescription = { gutsVisible ->
+ if (gutsVisible) {
+ gutsViewModel.gutsText
+ } else {
+ applicationContext.getString(
+ R.string.controls_media_playing_item_description,
+ model.songName,
+ model.artistName,
+ model.appName
+ )
+ }
+ },
+ backgroundCover = model.artwork,
+ appIcon = model.appIcon,
+ launcherIcon = getIconFromApp(model.packageName),
+ useGrayColorFilter = model.appIcon == null || model.isResume,
+ artistName = model.artistName ?: "",
+ titleName = model.songName ?: "",
+ isExplicitVisible = model.showExplicit,
+ shouldAddGradient = wallpaperColors != null,
+ colorScheme = scheme,
+ canShowTime = canShowScrubbingTimeViews(model.semanticActionButtons),
+ playTurbulenceNoise = playTurbulenceNoise,
+ useSemanticActions = model.semanticActionButtons != null,
+ actionButtons = toActionViewModels(model),
+ outputSwitcher = toOutputSwitcherViewModel(model),
+ gutsMenu = gutsViewModel,
+ onClicked = { expandable ->
+ model.clickIntent?.let { clickIntent ->
+ logger.logTapContentView(model.uid, model.packageName, model.instanceId)
+ // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
+ interactor.startClickIntent(expandable, clickIntent)
+ }
+ },
+ onLongClicked = {
+ logger.logLongPressOpen(model.uid, model.packageName, model.instanceId)
+ },
+ onSeek = {
+ logger.logSeek(model.uid, model.packageName, model.instanceId)
+ // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
+ },
+ onBindSeekbar = { seekBarViewModel ->
+ if (model.isResume && model.resumeProgress != null) {
+ seekBarViewModel.updateStaticProgress(model.resumeProgress)
+ } else {
+ backgroundExecutor.execute {
+ seekBarViewModel.updateController(
+ model.token?.let { MediaController(applicationContext, it) }
+ )
+ }
+ }
+ }
+ )
+ }
+
+ private fun toOutputSwitcherViewModel(model: MediaControlModel): MediaOutputSwitcherViewModel {
+ val device = model.deviceData
+ val showBroadcastButton = legacyLeAudioSharing() && device?.showBroadcastButton == true
+
+ // TODO(b/233698402): Use the package name instead of app label to avoid the unexpected
+ // result.
+ val isCurrentBroadcastApp =
+ device?.name?.let {
+ TextUtils.equals(
+ it,
+ applicationContext.getString(R.string.broadcasting_description_is_broadcasting)
+ )
+ }
+ ?: false
+ val useDisabledAlpha =
+ if (showBroadcastButton) {
+ !isCurrentBroadcastApp
+ } else {
+ device?.enabled == false || model.isResume
+ }
+ val deviceString =
+ device?.name
+ ?: if (showBroadcastButton) {
+ applicationContext.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name)
+ } else {
+ applicationContext.getString(R.string.media_seamless_other_device)
+ }
+ return MediaOutputSwitcherViewModel(
+ isTapEnabled = showBroadcastButton || !useDisabledAlpha,
+ deviceString = deviceString,
+ deviceIcon = device?.icon?.let { Icon.Loaded(it, null) }
+ ?: if (showBroadcastButton) {
+ Icon.Resource(R.drawable.settings_input_antenna, null)
+ } else {
+ Icon.Resource(R.drawable.ic_media_home_devices, null)
+ },
+ isCurrentBroadcastApp = isCurrentBroadcastApp,
+ isIntentValid = device?.intent != null,
+ alpha =
+ if (useDisabledAlpha) {
+ DISABLED_ALPHA
+ } else {
+ 1.0f
+ },
+ isVisible = showBroadcastButton,
+ onClicked = { expandable ->
+ if (showBroadcastButton) {
+ // If the current media app is not broadcasted and users press the outputer
+ // button, we should pop up the broadcast dialog to check do they want to
+ // switch broadcast to the other media app, otherwise we still pop up the
+ // media output dialog.
+ if (!isCurrentBroadcastApp) {
+ logger.logOpenBroadcastDialog(
+ model.uid,
+ model.packageName,
+ model.instanceId
+ )
+ interactor.startBroadcastDialog(
+ expandable,
+ device?.name.toString(),
+ model.packageName
+ )
+ } else {
+ logger.logOpenOutputSwitcher(model.uid, model.packageName, model.instanceId)
+ interactor.startMediaOutputDialog(expandable, model.packageName)
+ }
+ } else {
+ logger.logOpenOutputSwitcher(model.uid, model.packageName, model.instanceId)
+ device?.intent?.let { interactor.startDeviceIntent(it) }
+ ?: interactor.startMediaOutputDialog(expandable, model.packageName)
+ }
+ }
+ )
+ }
+
+ private fun toGutsViewModel(model: MediaControlModel, scheme: ColorScheme): GutsViewModel {
+ return GutsViewModel(
+ gutsText =
+ if (model.isDismissible) {
+ applicationContext.getString(
+ R.string.controls_media_close_session,
+ model.appName
+ )
+ } else {
+ applicationContext.getString(R.string.controls_media_active_session)
+ },
+ textPrimaryColor = textPrimaryFromScheme(scheme),
+ accentPrimaryColor = accentPrimaryFromScheme(scheme),
+ surfaceColor = surfaceFromScheme(scheme),
+ isDismissEnabled = model.isDismissible,
+ onDismissClicked = {
+ onDismissMediaData(model.token, model.uid, model.packageName, model.instanceId)
+ },
+ cancelTextBackground =
+ if (model.isDismissible) {
+ applicationContext.getDrawable(R.drawable.qs_media_outline_button)
+ } else {
+ applicationContext.getDrawable(R.drawable.qs_media_solid_button)
+ },
+ onSettingsClicked = {
+ logger.logLongPressSettings(model.uid, model.packageName, model.instanceId)
+ interactor.startSettings()
+ },
+ )
+ }
+
+ private fun toActionViewModels(model: MediaControlModel): List<MediaActionViewModel?> {
+ val semanticActionButtons =
+ model.semanticActionButtons?.let { mediaButton ->
+ with(mediaButton) {
+ val isScrubbingTimeEnabled = canShowScrubbingTimeViews(mediaButton)
+ SEMANTIC_ACTIONS_ALL.map { buttonId ->
+ getActionById(buttonId)?.let {
+ toSemanticActionViewModel(model, it, buttonId, isScrubbingTimeEnabled)
+ }
+ }
+ }
+ }
+ val notifActionButtons =
+ model.notificationActionButtons.mapIndexed { index, mediaAction ->
+ toNotifActionViewModel(model, mediaAction, index)
+ }
+ return semanticActionButtons ?: notifActionButtons
+ }
+
+ private fun toSemanticActionViewModel(
+ model: MediaControlModel,
+ mediaAction: MediaAction,
+ buttonId: Int,
+ canShowScrubbingTimeViews: Boolean
+ ): MediaActionViewModel {
+ val showInCollapsed = SEMANTIC_ACTIONS_COMPACT.contains(buttonId)
+ val hideWhenScrubbing = SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.contains(buttonId)
+ val shouldHideWhenScrubbing = canShowScrubbingTimeViews && hideWhenScrubbing
+ return MediaActionViewModel(
+ icon = mediaAction.icon,
+ contentDescription = mediaAction.contentDescription,
+ background = mediaAction.background,
+ isVisibleWhenScrubbing = !shouldHideWhenScrubbing,
+ notVisibleValue =
+ if (
+ (buttonId == R.id.actionPrev && model.semanticActionButtons!!.reservePrev) ||
+ (buttonId == R.id.actionNext && model.semanticActionButtons!!.reserveNext)
+ ) {
+ ConstraintSet.INVISIBLE
+ } else {
+ ConstraintSet.GONE
+ },
+ showInCollapsed = showInCollapsed,
+ rebindId = mediaAction.rebindId,
+ buttonId = buttonId,
+ isEnabled = mediaAction.action != null,
+ onClicked = { id ->
+ mediaAction.action?.let {
+ onButtonClicked(id, model.uid, model.packageName, model.instanceId, it)
+ }
+ },
+ )
+ }
+
+ private fun toNotifActionViewModel(
+ model: MediaControlModel,
+ mediaAction: MediaAction,
+ index: Int
+ ): MediaActionViewModel {
+ return MediaActionViewModel(
+ icon = mediaAction.icon,
+ contentDescription = mediaAction.contentDescription,
+ background = mediaAction.background,
+ showInCollapsed = model.actionsToShowInCollapsed.contains(index),
+ rebindId = mediaAction.rebindId,
+ isEnabled = mediaAction.action != null,
+ onClicked = { id ->
+ mediaAction.action?.let {
+ onButtonClicked(id, model.uid, model.packageName, model.instanceId, it)
+ }
+ },
+ )
+ }
+
+ private fun onButtonClicked(
+ id: Int,
+ uid: Int,
+ packageName: String,
+ instanceId: InstanceId,
+ action: Runnable
+ ) {
+ logger.logTapAction(id, uid, packageName, instanceId)
+ // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
+ isAnyButtonClicked.value = true
+ action.run()
+ }
+
+ private fun getIconFromApp(packageName: String): Icon {
+ return try {
+ Icon.Loaded(applicationContext.packageManager.getApplicationIcon(packageName), null)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(TAG, "Cannot find icon for package $packageName", e)
+ Icon.Resource(R.drawable.ic_music_note, null)
+ }
+ }
+
+ private fun canShowScrubbingTimeViews(semanticActions: MediaButton?): Boolean {
+ // The scrubbing time views replace the SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING action views,
+ // so we should only allow scrubbing times to be shown if those action views are present.
+ return semanticActions?.let {
+ SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.stream().allMatch { id: Int ->
+ semanticActions.getActionById(id) != null
+ }
+ }
+ ?: false
+ }
+
+ companion object {
+ private const val TAG = "MediaControlViewModel"
+ private const val MEDIA_PLAYER_ANIMATION_DELAY = 334L
+ private const val DISABLED_ALPHA = 0.38f
+
+ /** Buttons to show in small player when using semantic actions */
+ val SEMANTIC_ACTIONS_COMPACT =
+ listOf(R.id.actionPlayPause, R.id.actionPrev, R.id.actionNext)
+
+ /**
+ * Buttons that should get hidden when we are scrubbing (they will be replaced with the
+ * views showing scrubbing time)
+ */
+ val SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING = listOf(R.id.actionPrev, R.id.actionNext)
+
+ /** Buttons to show in player when using semantic actions. */
+ val SEMANTIC_ACTIONS_ALL =
+ listOf(
+ R.id.actionPlayPause,
+ R.id.actionPrev,
+ R.id.actionNext,
+ R.id.action0,
+ R.id.action1
+ )
+
+ const val TURBULENCE_NOISE_PLAY_MS_DURATION = 7500L
+ const val MEDIA_PLAYER_SCRIM_START_ALPHA = 0.25f
+ const val MEDIA_PLAYER_SCRIM_END_ALPHA = 1.0f
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaOutputSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaOutputSwitcherViewModel.kt
new file mode 100644
index 0000000..9df9bcc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaOutputSwitcherViewModel.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.ui.viewmodel
+
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.Icon
+
+/** Models UI state of output switcher chip. */
+data class MediaOutputSwitcherViewModel(
+ val isTapEnabled: Boolean,
+ val deviceString: CharSequence,
+ val deviceIcon: Icon,
+ val isCurrentBroadcastApp: Boolean,
+ val isIntentValid: Boolean,
+ val alpha: Float,
+ val isVisible: Boolean,
+ val onClicked: (Expandable) -> Unit,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
new file mode 100644
index 0000000..d1014e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.ui.viewmodel
+
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.monet.ColorScheme
+
+/** Models UI state for media player. */
+data class MediaPlayerViewModel(
+ val contentDescription: (Boolean) -> CharSequence,
+ val backgroundCover: android.graphics.drawable.Icon?,
+ val appIcon: android.graphics.drawable.Icon?,
+ val launcherIcon: Icon,
+ val useGrayColorFilter: Boolean,
+ val artistName: CharSequence,
+ val titleName: CharSequence,
+ val isExplicitVisible: Boolean,
+ val shouldAddGradient: Boolean,
+ val colorScheme: ColorScheme,
+ val canShowTime: Boolean,
+ val playTurbulenceNoise: Boolean,
+ val useSemanticActions: Boolean,
+ val actionButtons: List<MediaActionViewModel?>,
+ val outputSwitcher: MediaOutputSwitcherViewModel,
+ val gutsMenu: GutsViewModel,
+ val onClicked: (Expandable) -> Unit,
+ val onLongClicked: () -> Unit,
+ val onSeek: () -> Unit,
+ val onBindSeekbar: (SeekBarViewModel) -> Unit,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
index 19ea00d..a2307d4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
@@ -16,10 +16,8 @@
package com.android.systemui.media.controls.ui.viewmodel
-import android.app.WallpaperColors
import android.content.Context
import android.content.Intent
-import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.BitmapDrawable
@@ -122,7 +120,9 @@
return null
}
- val scheme = getColorScheme(model.packageName) ?: return null
+ val scheme =
+ MediaArtworkHelper.getColorScheme(applicationContext, model.packageName, TAG)
+ ?: return null
// Capture width & height from views in foreground for artwork scaling in background
val width =
@@ -213,9 +213,9 @@
return GutsViewModel(
gutsText =
applicationContext.getString(R.string.controls_media_close_session, model.appName),
- textColor = textPrimaryFromScheme(scheme),
- buttonBackgroundColor = accentPrimaryFromScheme(scheme),
- buttonTextColor = surfaceFromScheme(scheme),
+ textPrimaryColor = textPrimaryFromScheme(scheme),
+ accentPrimaryColor = accentPrimaryFromScheme(scheme),
+ surfaceColor = surfaceFromScheme(scheme),
onDismissClicked = {
onMediaRecommendationsDismissed(
model.key,
@@ -304,20 +304,6 @@
}
}
- private fun getColorScheme(packageName: String): ColorScheme? {
- // Set up recommendation card's header.
- return try {
- val packageManager = applicationContext.packageManager
- val applicationInfo = packageManager.getApplicationInfo(packageName, 0 /* flags */)
- // Set up media source app's logo.
- val icon = packageManager.getApplicationIcon(applicationInfo)
- ColorScheme(WallpaperColors.fromDrawable(icon), darkTheme = true)
- } catch (e: PackageManager.NameNotFoundException) {
- Log.w(TAG, "Fail to get media recommendation's app info", e)
- null
- }
- }
-
/** Returns a [Drawable] of a given [artworkIcon] scaled to [width]x[height] size, . */
private fun getScaledRecommendationCover(
artworkIcon: Icon,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt
index 452cb7e..ff8e903b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt
@@ -31,8 +31,9 @@
) {
/** Creates a [LocalMediaManager] for the given package. */
fun create(packageName: String?): LocalMediaManager {
- return InfoMediaManager.createInstance(context, packageName, localBluetoothManager).run {
- LocalMediaManager(context, localBluetoothManager, this, packageName)
- }
+ // TODO: b/321969740 - Populate the userHandle parameter in InfoMediaManager. The user
+ // handle is necessary to disambiguate the same package running on different users.
+ return InfoMediaManager.createInstance(context, packageName, null, localBluetoothManager)
+ .run { LocalMediaManager(context, localBluetoothManager, this, packageName) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index d4bd6da..4e77d13 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -21,16 +21,11 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import javax.inject.Inject
@SysUISingleton
-class MediaFlags
-@Inject
-constructor(
- private val featureFlags: FeatureFlagsClassic,
- private val sceneContainerFlags: SceneContainerFlags
-) {
+class MediaFlags @Inject constructor(private val featureFlags: FeatureFlagsClassic) {
/**
* Check whether media control actions should be based on PlaybackState instead of notification
*/
@@ -57,7 +52,7 @@
/** Check whether to use scene framework */
fun isSceneContainerEnabled() =
- sceneContainerFlags.isEnabled() && MediaInSceneContainerFlag.isEnabled
+ SceneContainerFlag.isEnabled && MediaInSceneContainerFlag.isEnabled
/** Check whether to use media refactor code */
fun isMediaControlsRefactorEnabled() = MediaControlsRefactorFlag.isEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogManager.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogManager.kt
index 54d175c..06267e2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogManager.kt
@@ -38,7 +38,9 @@
// Dismiss the previous dialog, if any.
mediaOutputBroadcastDialog?.dismiss()
- val controller = mediaOutputControllerFactory.create(packageName)
+ // TODO: b/321969740 - Populate the userHandle parameter. The user handle is necessary to
+ // disambiguate the same package running on different users.
+ val controller = mediaOutputControllerFactory.create(packageName, /* userHandle= */ null)
val dialog =
MediaOutputBroadcastDialog(context, aboveStatusBar, broadcastSender, controller)
mediaOutputBroadcastDialog = dialog
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 4db89d1..d6ca320 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -124,6 +124,7 @@
private static final String ALLOWLIST_REASON = "mediaoutput:remote_transfer";
private final String mPackageName;
+ private final UserHandle mUserHandle;
private final Context mContext;
private final MediaSessionManager mMediaSessionManager;
private final LocalBluetoothManager mLocalBluetoothManager;
@@ -177,6 +178,7 @@
public MediaOutputController(
Context context,
@Assisted String packageName,
+ @Assisted @Nullable UserHandle userHandle,
MediaSessionManager mediaSessionManager,
@Nullable LocalBluetoothManager lbm,
ActivityStarter starter,
@@ -190,6 +192,7 @@
UserTracker userTracker) {
mContext = context;
mPackageName = packageName;
+ mUserHandle = userHandle;
mMediaSessionManager = mediaSessionManager;
mLocalBluetoothManager = lbm;
mActivityStarter = starter;
@@ -199,7 +202,8 @@
mKeyGuardManager = keyGuardManager;
mFeatureFlags = featureFlags;
mUserTracker = userTracker;
- InfoMediaManager imm = InfoMediaManager.createInstance(mContext, packageName, lbm);
+ InfoMediaManager imm =
+ InfoMediaManager.createInstance(mContext, packageName, userHandle, lbm);
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
mDialogTransitionAnimator = dialogTransitionAnimator;
@@ -231,7 +235,7 @@
@AssistedFactory
public interface Factory {
/** Construct a MediaOutputController */
- MediaOutputController create(String packageName);
+ MediaOutputController create(String packageName, UserHandle userHandle);
}
protected void start(@NonNull Callback cb) {
@@ -946,11 +950,22 @@
}
void launchMediaOutputBroadcastDialog(View mediaOutputDialog, BroadcastSender broadcastSender) {
- MediaOutputController controller = new MediaOutputController(mContext, mPackageName,
- mMediaSessionManager, mLocalBluetoothManager, mActivityStarter,
- mNotifCollection, mDialogTransitionAnimator, mNearbyMediaDevicesManager,
- mAudioManager, mPowerExemptionManager, mKeyGuardManager, mFeatureFlags,
- mUserTracker);
+ MediaOutputController controller =
+ new MediaOutputController(
+ mContext,
+ mPackageName,
+ mUserHandle,
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mActivityStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyGuardManager,
+ mFeatureFlags,
+ mUserTracker);
MediaOutputBroadcastDialog dialog = new MediaOutputBroadcastDialog(mContext, true,
broadcastSender, controller);
dialog.show();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogManager.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogManager.kt
index e7816a4..04d1492 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogManager.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media.dialog
import android.content.Context
+import android.os.UserHandle
import android.view.View
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
@@ -41,7 +42,15 @@
}
/** Creates a [MediaOutputDialog] for the given package. */
- open fun createAndShow(packageName: String, aboveStatusBar: Boolean, view: View? = null) {
+ // TODO: b/321969740 - Make the userHandle non-optional, and place the parameter next to the
+ // package name. The user handle is necessary to disambiguate the same package running on
+ // different users.
+ open fun createAndShow(
+ packageName: String,
+ aboveStatusBar: Boolean,
+ view: View? = null,
+ userHandle: UserHandle? = null
+ ) {
createAndShowWithController(
packageName,
aboveStatusBar,
@@ -55,20 +64,26 @@
)
)
},
+ userHandle = userHandle,
)
}
/** Creates a [MediaOutputDialog] for the given package. */
+ // TODO: b/321969740 - Make the userHandle non-optional, and place the parameter next to the
+ // package name. The user handle is necessary to disambiguate the same package running on
+ // different users.
open fun createAndShowWithController(
packageName: String,
aboveStatusBar: Boolean,
controller: DialogTransitionAnimator.Controller?,
+ userHandle: UserHandle? = null,
) {
createAndShow(
packageName,
aboveStatusBar,
dialogTransitionAnimatorController = controller,
- includePlaybackAndAppMetadata = true
+ includePlaybackAndAppMetadata = true,
+ userHandle = userHandle,
)
}
@@ -79,20 +94,25 @@
packageName = null,
aboveStatusBar = false,
dialogTransitionAnimatorController = null,
- includePlaybackAndAppMetadata = false
+ includePlaybackAndAppMetadata = false,
+ userHandle = null,
)
}
+ // TODO: b/321969740 - Make the userHandle non-optional, and place the parameter next to the
+ // package name. The user handle is necessary to disambiguate the same package running on
+ // different users.
private fun createAndShow(
packageName: String?,
aboveStatusBar: Boolean,
dialogTransitionAnimatorController: DialogTransitionAnimator.Controller?,
- includePlaybackAndAppMetadata: Boolean = true
+ includePlaybackAndAppMetadata: Boolean = true,
+ userHandle: UserHandle? = null,
) {
// Dismiss the previous dialog, if any.
mediaOutputDialog?.dismiss()
- val controller = mediaOutputControllerFactory.create(packageName)
+ val controller = mediaOutputControllerFactory.create(packageName, userHandle)
val mediaOutputDialog =
MediaOutputDialog(
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java
index 6e7e0f2..9cc2888 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java
@@ -18,6 +18,7 @@
import android.annotation.MainThread;
import android.content.Context;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -52,9 +53,10 @@
@Override
@MainThread
- public void showMediaOutputSwitcher(String packageName) {
+ public void showMediaOutputSwitcher(String packageName, UserHandle userHandle) {
if (!TextUtils.isEmpty(packageName)) {
- mMediaOutputDialogManager.createAndShow(packageName, false, null);
+ mMediaOutputDialogManager.createAndShow(
+ packageName, /* aboveStatusBar= */ false, /* view= */ null, userHandle);
} else {
Log.e(TAG, "Unable to launch media output dialog. Package name is empty.");
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RemoteRecentSplitTaskTransitionRunner.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RemoteRecentSplitTaskTransitionRunner.kt
index 9514c4a..b7942f7 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RemoteRecentSplitTaskTransitionRunner.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RemoteRecentSplitTaskTransitionRunner.kt
@@ -27,8 +27,8 @@
import android.util.Log
import android.view.SurfaceControl
import android.view.animation.DecelerateInterpolator
-import android.window.IRemoteTransition
import android.window.IRemoteTransitionFinishedCallback
+import android.window.RemoteTransitionStub
import android.window.TransitionInfo
import android.window.WindowContainerToken
import com.android.app.viewcapture.ViewCapture
@@ -41,7 +41,7 @@
private val viewPosition: IntArray,
private val screenBounds: Rect,
private val handleResult: () -> Unit,
-) : IRemoteTransition.Stub() {
+) : RemoteTransitionStub() {
override fun startAnimation(
transition: IBinder?,
info: TransitionInfo?,
@@ -114,14 +114,6 @@
}
}
- override fun mergeAnimation(
- transition: IBinder?,
- info: TransitionInfo?,
- t: SurfaceControl.Transaction?,
- mergeTarget: IBinder?,
- finishedCallback: IRemoteTransitionFinishedCallback?
- ) {}
-
@Throws(RemoteException::class)
override fun onTransitionConsumed(transition: IBinder, aborted: Boolean) {
Log.w(TAG, "unexpected consumption of app selector transition: aborted=$aborted")
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index da9e00d..e861ddf 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -16,8 +16,11 @@
package com.android.systemui.mediaprojection.permission;
+import static android.Manifest.permission.LOG_COMPAT_CHANGE;
+import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
import static android.media.projection.IMediaProjectionManager.EXTRA_PACKAGE_REUSING_GRANTED_CONSENT;
import static android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW_GRANTED_CONSENT;
+import static android.media.projection.MediaProjectionManager.OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -26,11 +29,13 @@
import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.SINGLE_APP;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions.LaunchCookie;
import android.app.AlertDialog;
import android.app.StatusBarManager;
+import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -108,6 +113,7 @@
}
@Override
+ @RequiresPermission(allOf = {READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE})
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -235,6 +241,10 @@
// the correct screen width when in split screen.
Context dialogContext = getApplicationContext();
if (isPartialScreenSharingEnabled()) {
+ final boolean overrideDisableSingleAppOption =
+ CompatChanges.isChangeEnabled(
+ OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION,
+ mPackageName, getHostUserHandle());
MediaProjectionPermissionDialogDelegate delegate =
new MediaProjectionPermissionDialogDelegate(
dialogContext,
@@ -246,6 +256,7 @@
},
() -> finish(RECORD_CANCEL, /* projection= */ null),
appName,
+ overrideDisableSingleAppOption,
mUid,
mMediaProjectionMetricsLogger);
mDialog =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
index 0f54e93..8858041a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
@@ -30,11 +30,12 @@
private val onStartRecordingClicked: Consumer<MediaProjectionPermissionDialogDelegate>,
private val onCancelClicked: Runnable,
private val appName: String?,
+ private val forceShowPartialScreenshare: Boolean,
hostUid: Int,
mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
) :
BaseMediaProjectionPermissionDialogDelegate<AlertDialog>(
- createOptionList(context, appName, mediaProjectionConfig),
+ createOptionList(context, appName, mediaProjectionConfig, forceShowPartialScreenshare),
appName,
hostUid,
mediaProjectionMetricsLogger
@@ -65,7 +66,8 @@
private fun createOptionList(
context: Context,
appName: String?,
- mediaProjectionConfig: MediaProjectionConfig?
+ mediaProjectionConfig: MediaProjectionConfig?,
+ overrideDisableSingleAppOption: Boolean = false,
): List<ScreenShareOption> {
val singleAppWarningText =
if (appName == null) {
@@ -80,8 +82,13 @@
R.string.media_projection_entry_app_permission_dialog_warning_entire_screen
}
+ // The single app option should only be disabled if there is an app name provided,
+ // the client has setup a MediaProjection with
+ // MediaProjectionConfig#createConfigForDefaultDisplay, AND it hasn't been overridden by
+ // the OVERRIDE_DISABLE_SINGLE_APP_OPTION per-app override.
val singleAppOptionDisabled =
appName != null &&
+ !overrideDisableSingleAppOption &&
mediaProjectionConfig?.regionToCapture ==
MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 63989ef..a6b6d61 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -35,6 +35,7 @@
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Configuration;
import android.database.ContentObserver;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
@@ -74,6 +75,7 @@
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import dagger.Lazy;
@@ -101,7 +103,7 @@
AccessibilityButtonModeObserver.ModeChangedListener,
AccessibilityButtonTargetsObserver.TargetsChangedListener,
OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
- Dumpable, CommandQueue.Callbacks {
+ Dumpable, CommandQueue.Callbacks, ConfigurationController.ConfigurationListener {
private static final String TAG = NavBarHelper.class.getSimpleName();
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -189,6 +191,7 @@
UserTracker userTracker,
DisplayTracker displayTracker,
NotificationShadeWindowController notificationShadeWindowController,
+ ConfigurationController configurationController,
DumpManager dumpManager,
CommandQueue commandQueue,
@Main Executor mainExecutor) {
@@ -215,6 +218,7 @@
mNavBarMode = navigationModeController.addListener(this);
mCommandQueue.addCallback(this);
+ configurationController.addCallback(this);
overviewProxyService.addCallback(this);
dumpManager.registerDumpable(this);
}
@@ -359,6 +363,11 @@
updateA11yState();
}
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ mEdgeBackGestureHandler.onConfigurationChanged(newConfig);
+ }
+
/**
* Updates the current accessibility button state. The accessibility button state is only
* used for {@link Secure#ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR} and
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index ade56c4..43c73c4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -411,18 +411,16 @@
@Override
public void setOverrideHomeButtonLongPress(long duration, float slopMultiplier) {
+ Log.d(TAG, "setOverrideHomeButtonLongPress receives: " + duration + "; "
+ + slopMultiplier);
mOverrideHomeButtonLongPressDurationMs = Optional.of(duration)
.filter(value -> value > 0);
mOverrideHomeButtonLongPressSlopMultiplier = Optional.of(slopMultiplier)
.filter(value -> value > 0);
- if (mOverrideHomeButtonLongPressDurationMs.isPresent()) {
- Log.d(TAG, "Receive duration override: "
- + mOverrideHomeButtonLongPressDurationMs.get());
- }
- if (mOverrideHomeButtonLongPressSlopMultiplier.isPresent()) {
- Log.d(TAG, "Receive slop multiplier override: "
- + mOverrideHomeButtonLongPressSlopMultiplier.get());
- }
+ mOverrideHomeButtonLongPressDurationMs.ifPresent(aLong
+ -> Log.d(TAG, "Use duration override: " + aLong));
+ mOverrideHomeButtonLongPressSlopMultiplier.ifPresent(aFloat
+ -> Log.d(TAG, "Use slop multiplier override: " + aFloat));
if (mView != null) {
reconfigureHomeLongClick();
}
@@ -902,11 +900,6 @@
refreshLayout(ld);
}
repositionNavigationBar(rotation);
- // NOTE(b/260220098): In some cases, the recreated nav bar will already have the right
- // configuration, which means that NavBarView will not receive a configuration change to
- // propagate to EdgeBackGestureHandler (which is injected into this and NBV). As such, we
- // should also force-update the gesture handler to ensure it updates to the right bounds
- mEdgeBackGestureHandler.onConfigurationChanged(newConfig);
if (canShowSecondaryHandle()) {
if (rotation != mCurrentRotation) {
mCurrentRotation = rotation;
@@ -1400,9 +1393,10 @@
break;
case MotionEvent.ACTION_MOVE:
if (!mHandler.hasCallbacks(mOnVariableDurationHomeLongClick)) {
- Log.w(TAG, "No callback. Don't handle touch slop.");
+ Log.v(TAG, "ACTION_MOVE no callback. Don't handle touch slop.");
break;
}
+ Log.v(TAG, "ACTION_MOVE handle touch slop");
float customSlopMultiplier = mOverrideHomeButtonLongPressSlopMultiplier.orElse(1f);
float touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
float calculatedTouchSlop =
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 9f7d1b3..12f2703 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -162,14 +162,11 @@
mIsLargeScreen = isLargeScreen(mContext);
boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
boolean largeScreenChanged = mIsLargeScreen != isOldConfigLargeScreen;
- // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
+ // TODO(b/332635834): Disable this logging once b/332635834 is fixed.
Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
+ " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
+ " willApplyConfigToNavbars=" + willApplyConfig
+ " navBarCount=" + mNavigationBars.size());
- if (mTaskbarDelegate.isInitialized()) {
- mTaskbarDelegate.onConfigurationChanged(newConfig);
- }
// If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
if (largeScreenChanged && updateNavbarForTaskbar()) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 1927f49..3c69ed9 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -1031,7 +1031,6 @@
updateIcons(mTmpLastConfiguration);
updateRecentsIcon();
updateCurrentRotation();
- mEdgeBackGestureHandler.onConfigurationChanged(mConfiguration);
if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi
|| mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) {
// If car mode or density changes, we need to reset the icons.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 5dd1bd8..f67973b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -39,7 +39,6 @@
import android.app.StatusBarManager;
import android.app.StatusBarManager.WindowVisibleState;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
@@ -72,7 +71,6 @@
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -250,8 +248,6 @@
mLightBarController.setNavigationBar(mLightBarTransitionsController);
mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
mEdgeBackGestureHandler.setBackAnimation(mBackAnimation);
- mEdgeBackGestureHandler.onConfigurationChanged(
- mContext.getResources().getConfiguration());
mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
mInitialized = true;
} finally {
@@ -495,10 +491,6 @@
return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
}
- public void onConfigurationChanged(Configuration configuration) {
- mEdgeBackGestureHandler.onConfigurationChanged(configuration);
- }
-
@Override
public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
mOverviewProxyService.onNavigationBarLumaSamplingEnabled(displayId, enable);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 9d0ea5e..db4a7fa 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -1187,7 +1187,7 @@
updateDisabledForQuickstep(newConfig);
}
- // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
+ // TODO(b/332635834): Disable this logging once b/332635834 is fixed.
Log.i(DEBUG_MISSING_GESTURE_TAG, "Config changed: newConfig=" + newConfig
+ " lastReportedConfig=" + mLastReportedConfig);
mLastReportedConfig.updateFrom(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index 9698548d..54a59f30 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -38,7 +38,7 @@
import javax.inject.Inject
/** Class responsible to "glue" all note task dependencies. */
-internal class NoteTaskInitializer
+class NoteTaskInitializer
@Inject
constructor(
private val controller: NoteTaskController,
@@ -138,11 +138,12 @@
* Returns a [NoteTaskEntryPoint] if an action should be taken, and null otherwise.
*/
private fun KeyEvent.toNoteTaskEntryPointOrNull(): NoteTaskEntryPoint? {
- val entryPoint = when {
- keyCode == KEYCODE_STYLUS_BUTTON_TAIL && isTailButtonNotesGesture() -> TAIL_BUTTON
- keyCode == KEYCODE_N && isMetaPressed && isCtrlPressed -> KEYBOARD_SHORTCUT
- else -> null
- }
+ val entryPoint =
+ when {
+ keyCode == KEYCODE_STYLUS_BUTTON_TAIL && isTailButtonNotesGesture() -> TAIL_BUTTON
+ keyCode == KEYCODE_N && isMetaPressed && isCtrlPressed -> KEYBOARD_SHORTCUT
+ else -> null
+ }
debugLog { "toNoteTaskEntryPointOrNull: entryPoint=$entryPoint" }
return entryPoint
}
@@ -164,7 +165,9 @@
// For now, trigger action immediately on UP of a single press, without waiting for
// the multi-press timeout to expire.
- debugLog { "isTailButtonNotesGesture: isMultiPress=$isMultiPress, isLongPress=$isLongPress" }
+ debugLog {
+ "isTailButtonNotesGesture: isMultiPress=$isMultiPress, isLongPress=$isLongPress"
+ }
return !isMultiPress && !isLongPress
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index ffbc560..7ccdf0a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -24,7 +24,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;
@@ -66,15 +66,14 @@
QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController,
ConfigurationController configurationController,
- FalsingManager falsingManager,
- SceneContainerFlags sceneContainerFlags) {
+ FalsingManager falsingManager) {
super(view);
mQsPanelController = qsPanelController;
mQuickStatusBarHeaderController = quickStatusBarHeaderController;
mConfigurationController = configurationController;
mFalsingManager = falsingManager;
mQSPanelContainer = mView.getQSPanelContainer();
- mSceneContainerEnabled = sceneContainerFlags.isEnabled();
+ mSceneContainerEnabled = SceneContainerFlag.isEnabled();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index a0607e9..1f4838e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -60,7 +60,7 @@
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.brightness.MirrorController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.CommandQueue;
@@ -174,8 +174,6 @@
@Nullable
private View mFooterActionsView;
- private final SceneContainerFlags mSceneContainerFlags;
-
@Inject
public QSImpl(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
SysuiStatusBarStateController statusBarStateController, CommandQueue commandQueue,
@@ -188,8 +186,7 @@
FooterActionsViewModel.Factory footerActionsViewModelFactory,
FooterActionsViewBinder footerActionsViewBinder,
LargeScreenShadeInterpolator largeScreenShadeInterpolator,
- FeatureFlags featureFlags,
- SceneContainerFlags sceneContainerFlags) {
+ FeatureFlags featureFlags) {
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
mQsMediaHost = qsMediaHost;
mQqsMediaHost = qqsMediaHost;
@@ -205,8 +202,7 @@
mFooterActionsViewModelFactory = footerActionsViewModelFactory;
mFooterActionsViewBinder = footerActionsViewBinder;
mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
- mSceneContainerFlags = sceneContainerFlags;
- if (mSceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
mStatusBarState = StatusBarState.SHADE;
}
}
@@ -224,7 +220,7 @@
mQSPanelController.init();
mQuickQSPanelController.init();
- if (!mSceneContainerFlags.isEnabled()) {
+ if (!SceneContainerFlag.isEnabled()) {
mQSFooterActionsViewModel = mFooterActionsViewModelFactory
.create(mListeningAndVisibilityLifecycleOwner);
bindFooterActionsView(mRootView);
@@ -249,7 +245,7 @@
mScrollListener.onQsPanelScrollChanged(scrollY);
}
});
- mQSPanelScrollView.setScrollingEnabled(!mSceneContainerFlags.isEnabled());
+ mQSPanelScrollView.setScrollingEnabled(!SceneContainerFlag.isEnabled());
mHeader = mRootView.findViewById(R.id.header);
mFooter = qsComponent.getQSFooter();
@@ -509,7 +505,7 @@
@VisibleForTesting
boolean isKeyguardState() {
- if (mSceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
return false;
} else {
// We want the freshest state here since otherwise we'll have some weirdness if earlier
@@ -573,7 +569,7 @@
}
private void setKeyguardShowing(boolean keyguardShowing) {
- if (!mSceneContainerFlags.isEnabled()) {
+ if (!SceneContainerFlag.isEnabled()) {
if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
mLastQSExpansion = -1;
@@ -651,7 +647,7 @@
@Override
public int getHeightDiff() {
- if (mSceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
return mQSPanelController.getViewBottom() - mHeader.getBottom()
+ mHeader.getPaddingBottom();
} else {
@@ -720,7 +716,7 @@
mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
- if (!mSceneContainerFlags.isEnabled()) {
+ if (!SceneContainerFlag.isEnabled()) {
float qsScrollViewTranslation =
onKeyguard && !mShowCollapsedOnKeyguard ? panelTranslationY : 0;
mQSPanelScrollView.setTranslationY(qsScrollViewTranslation);
@@ -824,7 +820,7 @@
mQsBounds.set(-sideMargin, 0, mQSPanelScrollView.getWidth() + sideMargin,
mQSPanelScrollView.getHeight());
}
- if (!mSceneContainerFlags.isEnabled()) {
+ if (!SceneContainerFlag.isEnabled()) {
mQSPanelScrollView.setClipBounds(mQsBounds);
mQSPanelScrollView.getLocationOnScreen(mLocationTemp);
@@ -907,7 +903,7 @@
// The customize state changed, so our height changed.
mContainer.updateExpansion();
boolean customizing = isCustomizing();
- if (mSceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
mQSPanelController.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
} else {
mQSPanelScrollView.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
@@ -984,7 +980,7 @@
@Override
public void onStateChanged(int newState) {
- if (mSceneContainerFlags.isEnabled() || newState == mStatusBarState) {
+ if (SceneContainerFlag.isEnabled() || newState == mStatusBarState) {
return;
}
mStatusBarState = newState;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 55dc485..e24caf1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -29,6 +29,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.haptics.qs.QSLongPressEffect;
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.media.controls.ui.view.MediaHostState;
@@ -36,18 +37,18 @@
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.brightness.BrightnessController;
import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.settings.brightness.MirrorController;
-import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.tuner.TunerService;
import javax.inject.Inject;
import javax.inject.Named;
+import javax.inject.Provider;
/**
* Controller for {@link QSPanel}.
@@ -93,11 +94,10 @@
FalsingManager falsingManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
SplitShadeStateController splitShadeStateController,
- SceneContainerFlags sceneContainerFlags,
- VibratorHelper vibratorHelper) {
+ Provider<QSLongPressEffect> longPRessEffectProvider) {
super(view, qsHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager, splitShadeStateController,
- vibratorHelper);
+ longPRessEffectProvider);
mTunerService = tunerService;
mQsCustomizerController = qsCustomizerController;
mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
@@ -112,7 +112,7 @@
mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mLastDensity = view.getResources().getConfiguration().densityDpi;
- mSceneContainerEnabled = sceneContainerFlags.isEnabled();
+ mSceneContainerEnabled = SceneContainerFlag.isEnabled();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index d8e8187..583cfb9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import static com.android.systemui.Flags.quickSettingsVisualHapticsLongpress;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,6 +33,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.haptics.qs.QSLongPressEffect;
import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
@@ -39,7 +41,6 @@
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileViewImpl;
-import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.animation.DisappearParameters;
@@ -55,6 +56,8 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;
+import javax.inject.Provider;
+
/**
* Controller for QSPanel views.
*
@@ -88,7 +91,7 @@
private SplitShadeStateController mSplitShadeStateController;
- private final VibratorHelper mVibratorHelper;
+ private final Provider<QSLongPressEffect> mLongPressEffectProvider;
@VisibleForTesting
protected final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
@@ -148,7 +151,7 @@
QSLogger qsLogger,
DumpManager dumpManager,
SplitShadeStateController splitShadeStateController,
- VibratorHelper vibratorHelper
+ Provider<QSLongPressEffect> longPressEffectProvider
) {
super(view);
mHost = host;
@@ -162,7 +165,7 @@
mSplitShadeStateController = splitShadeStateController;
mShouldUseSplitNotificationShade =
mSplitShadeStateController.shouldUseSplitNotificationShade(getResources());
- mVibratorHelper = vibratorHelper;
+ mLongPressEffectProvider = longPressEffectProvider;
}
@Override
@@ -305,8 +308,14 @@
}
private void addTile(final QSTile tile, boolean collapsedView) {
+ QSLongPressEffect longPressEffect;
+ if (quickSettingsVisualHapticsLongpress()) {
+ longPressEffect = mLongPressEffectProvider.get();
+ } else {
+ longPressEffect = null;
+ }
final QSTileViewImpl tileView = new QSTileViewImpl(
- getContext(), collapsedView, mVibratorHelper);
+ getContext(), collapsedView, longPressEffect);
final TileRecord r = new TileRecord(tile, tileView);
// TODO(b/250618218): Remove the QSLogger in QSTileViewImpl once we know the root cause of
// b/250618218.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 05bb088..6cda740 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -25,6 +25,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.haptics.qs.QSLongPressEffect;
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
@@ -32,7 +33,6 @@
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.res.R;
-import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.leak.RotationUtils;
@@ -58,10 +58,11 @@
Provider<Boolean> usingCollapsedLandscapeMediaProvider,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
DumpManager dumpManager, SplitShadeStateController splitShadeStateController,
- VibratorHelper vibratorHelper
+ Provider<QSLongPressEffect> longPressEffectProvider
) {
super(view, qsHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
- uiEventLogger, qsLogger, dumpManager, splitShadeStateController, vibratorHelper);
+ uiEventLogger, qsLogger, dumpManager, splitShadeStateController,
+ longPressEffectProvider);
mUsingCollapsedLandscapeMediaProvider = usingCollapsedLandscapeMediaProvider;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 1d92d78..bb40d3e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -17,7 +17,7 @@
package com.android.systemui.qs;
import com.android.systemui.qs.dagger.QSScope;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -34,12 +34,11 @@
@Inject
QuickStatusBarHeaderController(QuickStatusBarHeader view,
- QuickQSPanelController quickQSPanelController,
- SceneContainerFlags sceneContainerFlags
+ QuickQSPanelController quickQSPanelController
) {
super(view);
mQuickQSPanelController = quickQSPanelController;
- mSceneContainerEnabled = sceneContainerFlags.isEnabled();
+ mSceneContainerEnabled = SceneContainerFlag.isEnabled();
}
@Override
protected void onViewAttached() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
index a01d658..10c8e53 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,124 +16,21 @@
package com.android.systemui.qs;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.hardware.display.ColorDisplayManager;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.provider.Settings;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.CallbackController;
-import com.android.systemui.util.settings.SecureSettings;
-import java.util.ArrayList;
-
-import javax.inject.Inject;
-
-/**
- * @hide
- */
-@SysUISingleton
-public class ReduceBrightColorsController implements
+public interface ReduceBrightColorsController extends
CallbackController<ReduceBrightColorsController.Listener> {
- private final ColorDisplayManager mManager;
- private final UserTracker mUserTracker;
- private UserTracker.Callback mCurrentUserTrackerCallback;
- private final Handler mHandler;
- private final ContentObserver mContentObserver;
- private final SecureSettings mSecureSettings;
- private final ArrayList<ReduceBrightColorsController.Listener> mListeners = new ArrayList<>();
-
- @Inject
- public ReduceBrightColorsController(UserTracker userTracker,
- @Background Handler handler,
- ColorDisplayManager colorDisplayManager,
- SecureSettings secureSettings) {
- mManager = colorDisplayManager;
- mUserTracker = userTracker;
- mHandler = handler;
- mSecureSettings = secureSettings;
- mContentObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- super.onChange(selfChange, uri);
- final String setting = uri == null ? null : uri.getLastPathSegment();
- synchronized (mListeners) {
- if (setting != null && mListeners.size() != 0) {
- if (setting.equals(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED)) {
- dispatchOnActivated(mManager.isReduceBrightColorsActivated());
- }
- }
- }
- }
- };
-
- mCurrentUserTrackerCallback = new UserTracker.Callback() {
- @Override
- public void onUserChanged(int newUser, Context userContext) {
- synchronized (mListeners) {
- if (mListeners.size() > 0) {
- mSecureSettings.unregisterContentObserver(mContentObserver);
- mSecureSettings.registerContentObserverForUser(
- Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
- false, mContentObserver, newUser);
- }
- }
- }
- };
- mUserTracker.addCallback(mCurrentUserTrackerCallback, new HandlerExecutor(handler));
- }
-
- @Override
- public void addCallback(@NonNull Listener listener) {
- synchronized (mListeners) {
- if (!mListeners.contains(listener)) {
- mListeners.add(listener);
- if (mListeners.size() == 1) {
- mSecureSettings.registerContentObserverForUser(
- Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
- false, mContentObserver, mUserTracker.getUserId());
- }
- }
- }
- }
-
- @Override
- public void removeCallback(@androidx.annotation.NonNull Listener listener) {
- synchronized (mListeners) {
- if (mListeners.remove(listener) && mListeners.size() == 0) {
- mSecureSettings.unregisterContentObserver(mContentObserver);
- }
- }
- }
/** Returns {@code true} if Reduce Bright Colors is activated */
- public boolean isReduceBrightColorsActivated() {
- return mManager.isReduceBrightColorsActivated();
- }
+ boolean isReduceBrightColorsActivated();
/** Sets the activation state of Reduce Bright Colors */
- public void setReduceBrightColorsActivated(boolean activated) {
- mManager.setReduceBrightColorsActivated(activated);
- }
-
- private void dispatchOnActivated(boolean activated) {
- ArrayList<Listener> copy = new ArrayList<>(mListeners);
- for (Listener l : copy) {
- l.onActivated(activated);
- }
- }
+ void setReduceBrightColorsActivated(boolean activated);
/**
* Listener invoked whenever the Reduce Bright Colors settings are changed.
*/
- public interface Listener {
+ interface Listener {
/**
* Listener invoked when the activated state changes.
*
@@ -142,4 +39,4 @@
default void onActivated(boolean activated) {
}
}
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
new file mode 100644
index 0000000..4fc6609
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+/**
+ * @hide
+ */
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.hardware.display.ColorDisplayManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.SecureSettings;
+
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+
+@SysUISingleton
+public class ReduceBrightColorsControllerImpl implements
+ ReduceBrightColorsController {
+ private final ColorDisplayManager mManager;
+ private final UserTracker mUserTracker;
+ private UserTracker.Callback mCurrentUserTrackerCallback;
+ private final Handler mHandler;
+ private final ContentObserver mContentObserver;
+ private final SecureSettings mSecureSettings;
+ private final ArrayList<ReduceBrightColorsController.Listener> mListeners = new ArrayList<>();
+
+ @Inject
+ public ReduceBrightColorsControllerImpl(UserTracker userTracker,
+ @Background Handler handler,
+ ColorDisplayManager colorDisplayManager,
+ SecureSettings secureSettings) {
+ mManager = colorDisplayManager;
+ mUserTracker = userTracker;
+ mHandler = handler;
+ mSecureSettings = secureSettings;
+ mContentObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ final String setting = uri == null ? null : uri.getLastPathSegment();
+ synchronized (mListeners) {
+ if (setting != null && mListeners.size() != 0) {
+ if (setting.equals(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED)) {
+ dispatchOnActivated(mManager.isReduceBrightColorsActivated());
+ }
+ }
+ }
+ }
+ };
+
+ mCurrentUserTrackerCallback = new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, Context userContext) {
+ synchronized (mListeners) {
+ if (mListeners.size() > 0) {
+ mSecureSettings.unregisterContentObserver(mContentObserver);
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
+ false, mContentObserver, newUser);
+ }
+ }
+ }
+ };
+ mUserTracker.addCallback(mCurrentUserTrackerCallback, new HandlerExecutor(handler));
+ }
+
+ @Override
+ public void addCallback(@NonNull Listener listener) {
+ synchronized (mListeners) {
+ if (!mListeners.contains(listener)) {
+ mListeners.add(listener);
+ if (mListeners.size() == 1) {
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
+ false, mContentObserver, mUserTracker.getUserId());
+ }
+ }
+ }
+ }
+
+ @Override
+ public void removeCallback(@androidx.annotation.NonNull Listener listener) {
+ synchronized (mListeners) {
+ if (mListeners.remove(listener) && mListeners.size() == 0) {
+ mSecureSettings.unregisterContentObserver(mContentObserver);
+ }
+ }
+ }
+
+ @Override
+ public boolean isReduceBrightColorsActivated() {
+ return mManager.isReduceBrightColorsActivated();
+ }
+
+ @Override
+ public void setReduceBrightColorsActivated(boolean activated) {
+ mManager.setReduceBrightColorsActivated(activated);
+ }
+
+ private void dispatchOnActivated(boolean activated) {
+ ArrayList<Listener> copy = new ArrayList<>(mListeners);
+ for (Listener l : copy) {
+ l.onActivated(activated);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index 34b1b2d..a222b3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -42,7 +42,7 @@
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -107,8 +107,7 @@
protected QSCustomizerController(QSCustomizer view, TileQueryHelper tileQueryHelper,
QSHost qsHost, TileAdapter tileAdapter, ScreenLifecycle screenLifecycle,
KeyguardStateController keyguardStateController, LightBarController lightBarController,
- ConfigurationController configurationController, UiEventLogger uiEventLogger,
- SceneContainerFlags sceneContainerFlags) {
+ ConfigurationController configurationController, UiEventLogger uiEventLogger) {
super(view);
mTileQueryHelper = tileQueryHelper;
mQsHost = qsHost;
@@ -118,7 +117,7 @@
mLightBarController = lightBarController;
mConfigurationController = configurationController;
mUiEventLogger = uiEventLogger;
- view.setSceneContainerEnabled(sceneContainerFlags.isEnabled());
+ view.setSceneContainerEnabled(SceneContainerFlag.isEnabled());
mToolbar = mView.findViewById(com.android.internal.R.id.action_bar);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 8d3500a..b705a03 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -28,6 +28,7 @@
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.ReduceBrightColorsController;
+import com.android.systemui.qs.ReduceBrightColorsControllerImpl;
import com.android.systemui.qs.external.QSExternalModule;
import com.android.systemui.qs.panels.dagger.PanelsModule;
import com.android.systemui.qs.pipeline.dagger.QSPipelineModule;
@@ -118,4 +119,11 @@
@Binds
QSSceneAdapter bindsQsSceneInteractor(QSSceneAdapterImpl impl);
+
+ /**
+ * Dims the screen
+ */
+ @Binds
+ ReduceBrightColorsController bindReduceBrightColorsController(
+ ReduceBrightColorsControllerImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddable.kt
index 267e2b7..9c1b857 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddable.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
+import android.view.accessibility.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.ReduceBrightColorsController
import com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE
@@ -58,7 +59,9 @@
override val autoAddTracking
get() =
- if (available) {
+ if (Flags.a11yQsShortcut()) {
+ AutoAddTracking.Disabled
+ } else if (available) {
super.autoAddTracking
} else {
AutoAddTracking.Disabled
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 3004485..ca71870 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -62,15 +62,15 @@
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
import com.android.systemui.res.R
-import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.children
+import kotlinx.coroutines.DisposableHandle
import java.util.Objects
private const val TAG = "QSTileViewImpl"
open class QSTileViewImpl @JvmOverloads constructor(
context: Context,
private val collapsed: Boolean = false,
- private val vibratorHelper: VibratorHelper? = null,
+ private val longPressEffect: QSLongPressEffect? = null,
) : QSTileView(context), HeightOverrideable, LaunchableView {
companion object {
@@ -180,15 +180,13 @@
private val locInScreen = IntArray(2)
/** Visuo-haptic long-press effects */
- private var longPressEffect: QSLongPressEffect? = null
- private val longPressEffectViewBinder = QSLongPressEffectViewBinder()
private var initialLongPressProperties: QSLongPressProperties? = null
private var finalLongPressProperties: QSLongPressProperties? = null
private val colorEvaluator = ArgbEvaluator.getInstance()
- val hasLongPressEffect: Boolean
- get() = longPressEffect != null
- @VisibleForTesting val isLongPressEffectBound: Boolean
- get() = longPressEffectViewBinder.isBound
+ val isLongPressEffectInitialized: Boolean
+ get() = longPressEffect?.hasInitialized == true
+ @VisibleForTesting
+ var longPressEffectHandle: DisposableHandle? = null
init {
val typedValue = TypedValue()
@@ -325,6 +323,13 @@
}
private fun updateHeight() {
+ // TODO(b/332900989): Find a more robust way of resetting the tile if not reset by the
+ // launch animation.
+ if (scaleX != 1f || scaleY != 1f) {
+ // The launch animation of a long-press effect did not reset the long-press effect so
+ // we must do it here
+ resetLongPressEffectProperties()
+ }
val actualHeight = if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
heightOverride
} else {
@@ -614,25 +619,26 @@
lastIconTint = icon.getColor(state)
// Long-press effects
- if (quickSettingsVisualHapticsLongpress()){
- if (state.handlesLongClick && maybeCreateAndInitializeLongPressEffect()) {
- // set the valid long-press effect as the touch listener
- showRippleEffect = false
+ if (state.handlesLongClick &&
+ longPressEffect?.initializeEffect(longPressEffectDuration) == true) {
+ // set the valid long-press effect as the touch listener
+ if (longPressEffectHandle == null) {
+ longPressEffectHandle =
+ QSLongPressEffectViewBinder.bind(this, longPressEffect, state.spec)
setOnTouchListener(longPressEffect)
- if (!longPressEffectViewBinder.isBound) {
- longPressEffectViewBinder.bind(this, state.spec, longPressEffect)
- }
- } else {
- // Long-press effects might have been enabled before but the new state does not
- // handle a long-press. In this case, we go back to the behaviour of a regular tile
- // and clean-up the resources
- longPressEffectViewBinder.dispose()
- showRippleEffect = isClickable
- setOnTouchListener(null)
- longPressEffect = null
- initialLongPressProperties = null
- finalLongPressProperties = null
}
+ showRippleEffect = false
+ initializeLongPressProperties()
+ } else {
+ // Long-press effects might have been enabled before but the new state does not
+ // handle a long-press. In this case, we go back to the behaviour of a regular tile
+ // and clean-up the resources
+ setOnTouchListener(null)
+ longPressEffectHandle?.dispose()
+ longPressEffectHandle = null
+ showRippleEffect = isClickable
+ initialLongPressProperties = null
+ finalLongPressProperties = null
}
}
@@ -824,7 +830,7 @@
private fun interpolateFloat(fraction: Float, start: Float, end: Float): Float =
start + fraction * (end - start)
- private fun resetLongPressEffectProperties() {
+ fun resetLongPressEffectProperties() {
scaleY = 1f
scaleX = 1f
for (child in children) {
@@ -842,27 +848,6 @@
icon.setTint(icon.mIcon as ImageView, lastIconTint)
}
- private fun maybeCreateAndInitializeLongPressEffect(): Boolean {
- // Don't setup the effect if the long-press duration is invalid
- val effectDuration = longPressEffectDuration
- if (effectDuration <= 0) {
- longPressEffect = null
- return false
- }
-
- initializeLongPressProperties()
- if (longPressEffect == null) {
- longPressEffect =
- QSLongPressEffect(
- vibratorHelper,
- effectDuration,
- )
- } else {
- longPressEffect?.resetWithDuration(effectDuration)
- }
- return true
- }
-
private fun initializeLongPressProperties() {
initialLongPressProperties =
QSLongPressProperties(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
index 6c9a8a4..5122e1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
@@ -28,6 +28,7 @@
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModelAdapter
+import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
import javax.inject.Inject
import javax.inject.Provider
@@ -57,7 +58,9 @@
val viewModel: QSTileViewModel =
when (val spec = TileSpec.create(tileSpec)) {
is TileSpec.CustomTileSpec -> createCustomTileViewModel(spec)
- is TileSpec.PlatformTileSpec -> tileMap[tileSpec]?.get()
+ // when using the stub, we default to old tile rather than adding the stub
+ is TileSpec.PlatformTileSpec ->
+ tileMap[tileSpec]?.get()?.takeIf { it !is StubQSTileViewModel }
is TileSpec.Invalid -> null
}
?: return null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt
new file mode 100644
index 0000000..98fd561
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.reducebrightness.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.ReduceBrightColorsController
+import com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.util.kotlin.isEnabled
+import javax.inject.Inject
+import javax.inject.Named
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+/** Observes reduce bright colors state changes providing the [ReduceBrightColorsTileModel]. */
+class ReduceBrightColorsTileDataInteractor
+@Inject
+constructor(
+ @Background private val bgCoroutineContext: CoroutineContext,
+ @Named(RBC_AVAILABLE) private val isAvailable: Boolean,
+ private val reduceBrightColorsController: ReduceBrightColorsController,
+) : QSTileDataInteractor<ReduceBrightColorsTileModel> {
+
+ override fun tileData(
+ user: UserHandle,
+ triggers: Flow<DataUpdateTrigger>
+ ): Flow<ReduceBrightColorsTileModel> {
+ return reduceBrightColorsController
+ .isEnabled()
+ .distinctUntilChanged()
+ .map { ReduceBrightColorsTileModel(it) }
+ .flowOn(bgCoroutineContext)
+ }
+ override fun availability(user: UserHandle): Flow<Boolean> = flowOf(isAvailable)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
new file mode 100644
index 0000000..762f863
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.reducebrightness.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.qs.ReduceBrightColorsController
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+
+/** Handles reduce bright colors tile clicks. */
+class ReduceBrightColorsTileUserActionInteractor
+@Inject
+constructor(
+ private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+ private val reduceBrightColorsController: ReduceBrightColorsController,
+) : QSTileUserActionInteractor<ReduceBrightColorsTileModel> {
+
+ override suspend fun handleInput(input: QSTileInput<ReduceBrightColorsTileModel>): Unit =
+ with(input) {
+ when (action) {
+ is QSTileUserAction.Click -> {
+ reduceBrightColorsController.setReduceBrightColorsActivated(
+ !input.data.isEnabled
+ )
+ }
+ is QSTileUserAction.LongClick -> {
+ qsTileIntentUserActionHandler.handle(
+ action.view,
+ Intent(Settings.ACTION_REDUCE_BRIGHT_COLORS_SETTINGS)
+ )
+ }
+ }
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/model/ReduceBrightColorsTileModel.kt
similarity index 69%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/model/ReduceBrightColorsTileModel.kt
index f6140f5..05e0f5d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/model/ReduceBrightColorsTileModel.kt
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.qs.tiles.impl.reducebrightness.domain.model
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+/**
+ * Reduce bright colors tile model.
+ *
+ * @param isEnabled is true when the reduce bright colors is enabled;
+ */
+@JvmInline value class ReduceBrightColorsTileModel(val isEnabled: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
new file mode 100644
index 0000000..fca93df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.reducebrightness.ui
+
+import android.content.res.Resources
+import android.service.quicksettings.Tile
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [ReduceBrightColorsTileModel] to [QSTileState]. */
+class ReduceBrightColorsTileMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Resources.Theme,
+) : QSTileDataToStateMapper<ReduceBrightColorsTileModel> {
+
+ override fun map(config: QSTileConfig, data: ReduceBrightColorsTileModel): QSTileState =
+ QSTileState.build(resources, theme, config.uiConfig) {
+ if (data.isEnabled) {
+ activationState = QSTileState.ActivationState.ACTIVE
+ icon = {
+ Icon.Loaded(
+ drawable = resources.getDrawable(R.drawable.qs_extra_dim_icon_on, theme),
+ contentDescription = null
+ )
+ }
+
+ secondaryLabel =
+ resources
+ .getStringArray(R.array.tile_states_reduce_brightness)[Tile.STATE_ACTIVE]
+ } else {
+ activationState = QSTileState.ActivationState.INACTIVE
+ icon = {
+ Icon.Loaded(
+ drawable = resources.getDrawable(R.drawable.qs_extra_dim_icon_off, theme),
+ contentDescription = null
+ )
+ }
+ secondaryLabel =
+ resources
+ .getStringArray(R.array.tile_states_reduce_brightness)[Tile.STATE_INACTIVE]
+ }
+ label =
+ resources.getString(com.android.internal.R.string.reduce_bright_colors_feature_name)
+ contentDescription = label
+ supportedActions =
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractor.kt
new file mode 100644
index 0000000..85d2e3b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractor.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.model.ScreenRecordTileModel
+import com.android.systemui.screenrecord.RecordingController
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onStart
+
+/** Observes screen record state changes providing the [ScreenRecordTileModel]. */
+class ScreenRecordTileDataInteractor
+@Inject
+constructor(
+ @Background private val bgCoroutineContext: CoroutineContext,
+ private val recordingController: RecordingController,
+) : QSTileDataInteractor<ScreenRecordTileModel> {
+
+ override fun tileData(
+ user: UserHandle,
+ triggers: Flow<DataUpdateTrigger>
+ ): Flow<ScreenRecordTileModel> =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val callback =
+ object : RecordingController.RecordingStateChangeCallback {
+ override fun onRecordingStart() {
+ trySend(ScreenRecordTileModel.Recording)
+ }
+ override fun onRecordingEnd() {
+ trySend(ScreenRecordTileModel.DoingNothing)
+ }
+ override fun onCountdown(millisUntilFinished: Long) {
+ trySend(ScreenRecordTileModel.Starting(millisUntilFinished))
+ }
+ override fun onCountdownEnd() {
+ if (
+ !recordingController.isRecording && !recordingController.isStarting
+ ) {
+ // The tile was in Starting state and got canceled before recording
+ trySend(ScreenRecordTileModel.DoingNothing)
+ }
+ }
+ }
+ recordingController.addCallback(callback)
+ awaitClose { recordingController.removeCallback(callback) }
+ }
+ .onStart { emit(generateModel()) }
+ .distinctUntilChanged()
+ .flowOn(bgCoroutineContext)
+
+ override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
+
+ private fun generateModel(): ScreenRecordTileModel {
+ if (recordingController.isRecording) {
+ return ScreenRecordTileModel.Recording
+ } else if (recordingController.isStarting) {
+ return ScreenRecordTileModel.Starting(0)
+ } else {
+ return ScreenRecordTileModel.DoingNothing
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
new file mode 100644
index 0000000..d2bd09f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor
+
+import android.content.Context
+import android.util.Log
+import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogTransitionAnimator
+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.FeatureFlagsClassic
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.model.ScreenRecordTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.screenrecord.RecordingController
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+/** Handles screen recorder tile clicks. */
+class ScreenRecordTileUserActionInteractor
+@Inject
+constructor(
+ @Application private val context: Context,
+ @Main private val mainContext: CoroutineContext,
+ @Background private val backgroundContext: CoroutineContext,
+ private val recordingController: RecordingController,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardDismissUtil: KeyguardDismissUtil,
+ private val dialogTransitionAnimator: DialogTransitionAnimator,
+ private val panelInteractor: PanelInteractor,
+ private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
+ private val featureFlags: FeatureFlagsClassic,
+ private val activityStarter: ActivityStarter,
+) : QSTileUserActionInteractor<ScreenRecordTileModel> {
+ override suspend fun handleInput(input: QSTileInput<ScreenRecordTileModel>): Unit =
+ with(input) {
+ when (action) {
+ is QSTileUserAction.Click -> {
+ when (data) {
+ is ScreenRecordTileModel.Starting -> {
+ Log.d(TAG, "Cancelling countdown")
+ withContext(backgroundContext) { recordingController.cancelCountdown() }
+ }
+ is ScreenRecordTileModel.Recording ->
+ withContext(backgroundContext) { recordingController.stopRecording() }
+ is ScreenRecordTileModel.DoingNothing ->
+ withContext(mainContext) { showPrompt(action.view, user.identifier) }
+ }
+ }
+ is QSTileUserAction.LongClick -> {} // no-op
+ }
+ }
+
+ private fun showPrompt(view: View?, userId: Int) {
+ // Create the recording dialog that will collapse the shade only if we start the recording.
+ val onStartRecordingClicked = Runnable {
+ // We dismiss the shade. Since starting the recording will also dismiss the dialog, we
+ // disable the exit animation which looks weird when it happens at the same time as the
+ // shade collapsing.
+ dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations()
+ panelInteractor.collapsePanels()
+ }
+
+ val dialog =
+ recordingController.createScreenRecordDialog(
+ context,
+ featureFlags,
+ dialogTransitionAnimator,
+ activityStarter,
+ onStartRecordingClicked
+ )
+
+ if (dialog == null) {
+ Log.w(TAG, "showPrompt: dialog was null")
+ return
+ }
+
+ // We animate from the touched view only if we are not on the keyguard, given that if we
+ // are we will dismiss it which will also collapse the shade.
+ val shouldAnimateFromView = view != null && !keyguardInteractor.isKeyguardShowing()
+ val dismissAction =
+ ActivityStarter.OnDismissAction {
+ if (shouldAnimateFromView) {
+ dialogTransitionAnimator.showFromView(
+ dialog,
+ view!!,
+ DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG
+ ),
+ animateBackgroundBoundsChange = true
+ )
+ } else {
+ dialog.show()
+ }
+ mediaProjectionMetricsLogger.notifyPermissionRequestDisplayed(userId)
+ false
+ }
+
+ keyguardDismissUtil.executeWhenUnlocked(
+ dismissAction,
+ false /* requiresShadeOpen */,
+ true /* afterKeyguardDone */
+ )
+ }
+
+ private companion object {
+ const val TAG = "ScreenRecordTileUserActionInteractor"
+ const val INTERACTION_JANK_TAG = "screen_record"
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/model/ScreenRecordTileModel.kt
similarity index 64%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/model/ScreenRecordTileModel.kt
index f6140f5..26b0b01 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/model/ScreenRecordTileModel.kt
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.qs.tiles.impl.screenrecord.domain.model
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+/** Data model for screen record tile */
+sealed interface ScreenRecordTileModel {
+ data object Recording : ScreenRecordTileModel
+ data class Starting(val millisUntilStarted: Long) : ScreenRecordTileModel
+ data object DoingNothing : ScreenRecordTileModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
new file mode 100644
index 0000000..c09b0e3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.screenrecord.domain.ui
+
+import android.content.res.Resources
+import android.text.TextUtils
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.model.ScreenRecordTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [ScreenRecordTileModel] to [QSTileState]. */
+class ScreenRecordTileMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Resources.Theme,
+) : QSTileDataToStateMapper<ScreenRecordTileModel> {
+ override fun map(config: QSTileConfig, data: ScreenRecordTileModel): QSTileState =
+ QSTileState.build(resources, theme, config.uiConfig) {
+ label = resources.getString(R.string.quick_settings_screen_record_label)
+ supportedActions = setOf(QSTileState.UserAction.CLICK)
+
+ when (data) {
+ is ScreenRecordTileModel.Recording -> {
+ activationState = QSTileState.ActivationState.ACTIVE
+ val loadedIcon =
+ Icon.Loaded(
+ resources.getDrawable(R.drawable.qs_screen_record_icon_on, theme),
+ contentDescription = null
+ )
+ icon = { loadedIcon }
+ sideViewIcon = QSTileState.SideViewIcon.None
+ secondaryLabel = resources.getString(R.string.quick_settings_screen_record_stop)
+ }
+ is ScreenRecordTileModel.Starting -> {
+ activationState = QSTileState.ActivationState.ACTIVE
+ val loadedIcon =
+ Icon.Loaded(
+ resources.getDrawable(R.drawable.qs_screen_record_icon_on, theme),
+ contentDescription = null
+ )
+ icon = { loadedIcon }
+ val countDown = Math.floorDiv(data.millisUntilStarted + 500, 1000)
+ sideViewIcon = QSTileState.SideViewIcon.None
+ secondaryLabel = String.format("%d...", countDown)
+ }
+ is ScreenRecordTileModel.DoingNothing -> {
+ activationState = QSTileState.ActivationState.INACTIVE
+ val loadedIcon =
+ Icon.Loaded(
+ resources.getDrawable(R.drawable.qs_screen_record_icon_off, theme),
+ contentDescription = null
+ )
+ icon = { loadedIcon }
+ sideViewIcon = QSTileState.SideViewIcon.Chevron // tapping will open dialog
+ secondaryLabel =
+ resources.getString(R.string.quick_settings_screen_record_start)
+ }
+ }
+ contentDescription =
+ if (TextUtils.isEmpty(secondaryLabel)) label
+ else TextUtils.concat(label, ", ", secondaryLabel)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt
new file mode 100644
index 0000000..64f1fd3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.viewmodel
+
+import android.os.UserHandle
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+
+object StubQSTileViewModel : QSTileViewModel {
+
+ override val state: SharedFlow<QSTileState>
+ get() = error("Don't call stubs")
+
+ override val config: QSTileConfig
+ get() = error("Don't call stubs")
+
+ override val isAvailable: StateFlow<Boolean>
+ get() = error("Don't call stubs")
+
+ override fun onUserChanged(user: UserHandle) = error("Don't call stubs")
+
+ override fun forceUpdate() = error("Don't call stubs")
+
+ override fun onActionPerformed(userAction: QSTileUserAction) = error("Don't call stubs")
+
+ override fun destroy() = error("Don't call stubs")
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
index 4e0b576..ab0b0b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
@@ -52,8 +52,6 @@
val destinationScenes =
qsSceneAdapter.isCustomizing.flatMapLatest { customizing ->
if (customizing) {
- // TODO(b/332749288) Empty map so there are no back handlers and back can close
- // customizer
flowOf(emptyMap())
// TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade
// while customizing
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 4ece7b6..1ddc094 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -98,7 +98,7 @@
import com.android.systemui.navigationbar.buttons.KeyButtonView;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
@@ -146,7 +146,6 @@
private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
private final Context mContext;
- private final SceneContainerFlags mSceneContainerFlags;
private final Executor mMainExecutor;
private final ShellInterface mShellInterface;
private final Lazy<ShadeViewController> mShadeViewControllerLazy;
@@ -208,7 +207,7 @@
@Override
public void onStatusBarTouchEvent(MotionEvent event) {
verifyCallerAndClearCallingIdentity("onStatusBarTouchEvent", () -> {
- if (mSceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
//TODO(b/329863123) implement latency tracking for shade scene
Log.i(TAG_OPS, "Scene container enabled. Latency tracking not started.");
} else if (event.getActionMasked() == ACTION_DOWN) {
@@ -223,7 +222,7 @@
// If scene framework is enabled, set the scene container window to
// visible and let the touch "slip" into that window.
- if (mSceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
mSceneInteractor.get().onRemoteUserInteractionStarted("launcher swipe");
} else {
mShadeViewControllerLazy.get().startInputFocusTransfer();
@@ -232,7 +231,7 @@
if (action == ACTION_UP || action == ACTION_CANCEL) {
mInputFocusTransferStarted = false;
- if (!mSceneContainerFlags.isEnabled()) {
+ if (!SceneContainerFlag.isEnabled()) {
float velocity = (event.getY() - mInputFocusTransferStartY)
/ (event.getEventTime() - mInputFocusTransferStartMillis);
if (action == ACTION_CANCEL) {
@@ -612,7 +611,6 @@
KeyguardUnlockAnimationController sysuiUnlockAnimationController,
InWindowLauncherUnlockAnimationManager inWindowLauncherUnlockAnimationManager,
AssistUtils assistUtils,
- SceneContainerFlags sceneContainerFlags,
DumpManager dumpManager,
Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder,
BroadcastDispatcher broadcastDispatcher
@@ -624,7 +622,6 @@
}
mContext = context;
- mSceneContainerFlags = sceneContainerFlags;
mMainExecutor = mainExecutor;
mShellInterface = shellInterface;
mShadeViewControllerLazy = shadeViewControllerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index afd0746..8277c73 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -16,7 +16,6 @@
package com.android.systemui.scene
-import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
import dagger.Module
@@ -29,7 +28,6 @@
EmptySceneModule::class,
GoneSceneModule::class,
QuickSettingsSceneModule::class,
- SceneContainerFlagsModule::class,
ShadeSceneModule::class,
],
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 62b0914..69f9443 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -20,7 +20,6 @@
import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlagsModule
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.scene.domain.startable.SceneContainerStartable
-import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
import dagger.Binds
@@ -40,7 +39,6 @@
GoneSceneModule::class,
LockscreenSceneModule::class,
QuickSettingsSceneModule::class,
- SceneContainerFlagsModule::class,
ShadeSceneModule::class,
],
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
index 0665c9e..d202c24 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
@@ -16,7 +16,6 @@
package com.android.systemui.scene
-import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
import dagger.Module
@@ -30,7 +29,6 @@
EmptySceneModule::class,
GoneSceneModule::class,
LockscreenSceneModule::class,
- SceneContainerFlagsModule::class,
],
)
object ShadelessSceneContainerFrameworkModule {
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
index c736707..1cf1c18 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
@@ -24,7 +24,7 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
@@ -53,7 +53,6 @@
private val headsUpManager: HeadsUpManager,
private val powerInteractor: PowerInteractor,
private val activeNotificationsInteractor: ActiveNotificationsInteractor,
- sceneContainerFlags: SceneContainerFlags,
sceneInteractorProvider: Provider<SceneInteractor>,
) : CoreStartable {
@@ -68,7 +67,7 @@
* false if the bouncer is visible.
*/
val isLockscreenOrShadeVisible: StateFlow<Boolean> =
- if (!sceneContainerFlags.isEnabled()) {
+ if (!SceneContainerFlag.isEnabled) {
windowRootViewVisibilityRepository.isLockscreenOrShadeVisible
} else {
sceneInteractorProvider
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 32d72e0..4774eb3 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
@@ -44,9 +44,10 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -61,6 +62,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -68,10 +70,12 @@
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/**
@@ -88,7 +92,6 @@
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
private val bouncerInteractor: BouncerInteractor,
private val keyguardInteractor: KeyguardInteractor,
- private val flags: SceneContainerFlags,
private val sysUiState: SysUiState,
@DisplayId private val displayId: Int,
private val sceneLogger: SceneLogger,
@@ -103,10 +106,11 @@
private val headsUpInteractor: HeadsUpNotificationInteractor,
private val occlusionInteractor: SceneContainerOcclusionInteractor,
private val faceUnlockInteractor: DeviceEntryFaceAuthInteractor,
+ private val shadeInteractor: ShadeInteractor,
) : CoreStartable {
override fun start() {
- if (flags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
sceneLogger.logFrameworkEnabled(isEnabled = true)
hydrateVisibility()
automaticallySwitchScenes()
@@ -119,16 +123,18 @@
} else {
sceneLogger.logFrameworkEnabled(
isEnabled = false,
- reason = flags.requirementDescription(),
+ reason = SceneContainerFlag.requirementDescription(),
)
}
}
override fun dump(pw: PrintWriter, args: Array<out String>) =
pw.asIndenting().run {
- printSection("SceneContainerFlags") {
- println("isEnabled", flags.isEnabled())
- printSection("requirementDescription") { println(flags.requirementDescription()) }
+ printSection("SceneContainerFlag") {
+ println("isEnabled", SceneContainerFlag.isEnabled)
+ printSection("requirementDescription") {
+ println(SceneContainerFlag.requirementDescription())
+ }
}
}
@@ -185,6 +191,14 @@
/** Switches between scenes based on ever-changing application state. */
private fun automaticallySwitchScenes() {
+ handleBouncerImeVisibility()
+ handleSimUnlock()
+ handleDeviceUnlockStatus()
+ handlePowerState()
+ handleShadeTouchability()
+ }
+
+ private fun handleBouncerImeVisibility() {
applicationScope.launch {
// TODO (b/308001302): Move this to a bouncer specific interactor.
bouncerInteractor.onImeHiddenByUser.collectLatest {
@@ -196,6 +210,9 @@
}
}
}
+ }
+
+ private fun handleSimUnlock() {
applicationScope.launch {
simBouncerInteractor
.get()
@@ -229,7 +246,16 @@
}
}
}
+ }
+
+ private fun handleDeviceUnlockStatus() {
applicationScope.launch {
+ // Track the previous scene (sans Bouncer), so that we know where to go when the device
+ // is unlocked whilst on the bouncer.
+ val previousScene =
+ sceneInteractor.previousScene
+ .filterNot { it == Scenes.Bouncer }
+ .stateIn(this, SharingStarted.Eagerly, initialValue = null)
deviceUnlockedInteractor.deviceUnlockStatus
.mapNotNull { deviceUnlockStatus ->
val renderedScenes =
@@ -257,8 +283,16 @@
when {
isOnBouncer ->
- // When the device becomes unlocked in Bouncer, go to Gone.
- Scenes.Gone to "device was unlocked in Bouncer scene"
+ // When the device becomes unlocked in Bouncer, go to previous scene,
+ // or Gone.
+ if (previousScene.value == Scenes.Lockscreen) {
+ Scenes.Gone to "device was unlocked in Bouncer scene"
+ } else {
+ val prevScene = previousScene.value
+ (prevScene
+ ?: Scenes.Gone) to
+ "device was unlocked in Bouncer scene, from sceneKey=$prevScene"
+ }
isOnLockscreen ->
// The lockscreen should be dismissed automatically in 2 scenarios:
// 1. When face auth bypass is enabled and authentication happens while
@@ -288,7 +322,9 @@
)
}
}
+ }
+ private fun handlePowerState() {
applicationScope.launch {
powerInteractor.isAsleep.collect { isAsleep ->
if (isAsleep) {
@@ -317,7 +353,7 @@
) {
switchToScene(
targetSceneKey = Scenes.Bouncer,
- loggingReason = "device is starting to wake up with a locked sim"
+ loggingReason = "device is starting to wake up with a locked sim",
)
}
}
@@ -325,6 +361,20 @@
}
}
+ private fun handleShadeTouchability() {
+ applicationScope.launch {
+ shadeInteractor.isShadeTouchable
+ .distinctUntilChanged()
+ .filter { !it }
+ .collect {
+ switchToScene(
+ targetSceneKey = Scenes.Lockscreen,
+ loggingReason = "device became non-interactive",
+ )
+ }
+ }
+ }
+
/** Keeps [SysUiState] up-to-date */
private fun hydrateSystemUiState() {
applicationScope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
similarity index 83%
rename from packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
rename to packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
index c929196..234eda8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
@@ -20,18 +20,17 @@
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.Flags.sceneContainer
-import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.ComposeLockscreen
+import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent
import com.android.systemui.media.controls.util.MediaInSceneContainerFlag
import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
import com.android.systemui.statusbar.phone.PredictiveBackSysUiFlag
-import dagger.Module
-import dagger.Provides
/** Helper for reading or using the scene container flag state. */
object SceneContainerFlag {
@@ -48,7 +47,9 @@
MediaInSceneContainerFlag.isEnabled &&
MigrateClocksToBlueprint.isEnabled &&
NotificationsHeadsUpRefactor.isEnabled &&
- PredictiveBackSysUiFlag.isEnabled
+ PredictiveBackSysUiFlag.isEnabled &&
+ DeviceEntryUdfpsRefactor.isEnabled &&
+ RefactorKeyguardDismissIntent.isEnabled
// NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
/** The main aconfig flag. */
@@ -64,6 +65,8 @@
MigrateClocksToBlueprint.token,
NotificationsHeadsUpRefactor.token,
PredictiveBackSysUiFlag.token,
+ DeviceEntryUdfpsRefactor.token,
+ RefactorKeyguardDismissIntent.token,
// NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
)
@@ -93,30 +96,12 @@
*/
@JvmStatic
inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, DESCRIPTION)
-}
-
-/**
- * Defines interface for classes that can check whether the scene container framework feature is
- * enabled.
- */
-interface SceneContainerFlags {
-
- /** Returns `true` if the Scene Container Framework is enabled; `false` otherwise. */
- fun isEnabled(): Boolean
/** Returns a developer-readable string that describes the current requirement list. */
- fun requirementDescription(): String
-}
-
-class SceneContainerFlagsImpl : SceneContainerFlags {
-
- override fun isEnabled(): Boolean {
- return SceneContainerFlag.isEnabled
- }
-
- override fun requirementDescription(): String {
+ @JvmStatic
+ fun requirementDescription(): String {
return buildString {
- SceneContainerFlag.getAllRequirements().forEach { requirement ->
+ getAllRequirements().forEach { requirement ->
append('\n')
append(if (requirement.isEnabled) " [MET]" else "[NOT MET]")
append(" ${requirement.name}")
@@ -124,9 +109,3 @@
}
}
}
-
-@Module
-object SceneContainerFlagsModule {
-
- @Provides @SysUISingleton fun impl(): SceneContainerFlags = SceneContainerFlagsImpl()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index 67dc0cc..259a8bf 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -4,7 +4,6 @@
import android.util.AttributeSet
import android.view.View
import android.view.WindowInsets
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
@@ -31,7 +30,6 @@
viewModel: SceneContainerViewModel,
containerConfig: SceneContainerConfig,
sharedNotificationContainer: SharedNotificationContainer,
- flags: SceneContainerFlags,
scenes: Set<Scene>,
layoutInsetController: LayoutInsetsController,
sceneDataSourceDelegator: SceneDataSourceDelegator,
@@ -44,7 +42,6 @@
windowInsets = windowInsets,
containerConfig = containerConfig,
sharedNotificationContainer = sharedNotificationContainer,
- flags = flags,
scenes = scenes,
onVisibilityChangedInternal = { isVisible ->
super.setVisibility(if (isVisible) View.VISIBLE else View.INVISIBLE)
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 809ac2e..2ef9b73 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
@@ -39,7 +39,7 @@
import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
@@ -63,7 +63,6 @@
windowInsets: StateFlow<WindowInsets?>,
containerConfig: SceneContainerConfig,
sharedNotificationContainer: SharedNotificationContainer,
- flags: SceneContainerFlags,
scenes: Set<Scene>,
onVisibilityChangedInternal: (isVisible: Boolean) -> Unit,
dataSourceDelegator: SceneDataSourceDelegator,
@@ -115,7 +114,7 @@
// the SceneContainerView. This SharedNotificationContainer should contain NSSL
// due to the NotificationStackScrollLayoutSection (legacy) or
// NotificationSection (scene container) moving it there.
- if (flags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
(sharedNotificationContainer.parent as? ViewGroup)?.removeView(
sharedNotificationContainer
)
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
index 0bc02ed..8c675e3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
@@ -16,10 +16,22 @@
package com.android.systemui.screenrecord
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.ScreenRecordTile
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor.ScreenRecordTileDataInteractor
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor.ScreenRecordTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.model.ScreenRecordTileModel
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.ScreenRecordTileMapper
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
+import dagger.Provides
import dagger.multibindings.IntoMap
import dagger.multibindings.StringKey
@@ -30,4 +42,39 @@
@IntoMap
@StringKey(ScreenRecordTile.TILE_SPEC)
fun bindScreenRecordTile(screenRecordTile: ScreenRecordTile): QSTileImpl<*>
+
+ companion object {
+ private const val SCREEN_RECORD_TILE_SPEC = "screenrecord"
+
+ @Provides
+ @IntoMap
+ @StringKey(SCREEN_RECORD_TILE_SPEC)
+ fun provideScreenRecordTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+ QSTileConfig(
+ tileSpec = TileSpec.create(SCREEN_RECORD_TILE_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = R.drawable.qs_screen_record_icon_off,
+ labelRes = R.string.quick_settings_screen_record_label,
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ )
+
+ /** Inject ScreenRecord Tile into tileViewModelMap in QSModule */
+ @Provides
+ @IntoMap
+ @StringKey(SCREEN_RECORD_TILE_SPEC)
+ fun provideScreenRecordTileViewModel(
+ factory: QSTileViewModelFactory.Static<ScreenRecordTileModel>,
+ mapper: ScreenRecordTileMapper,
+ stateInteractor: ScreenRecordTileDataInteractor,
+ userActionInteractor: ScreenRecordTileUserActionInteractor
+ ): QSTileViewModel =
+ factory.create(
+ TileSpec.create(SCREEN_RECORD_TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java b/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
index ab8fc65..12bff49 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.screenshot;
+import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.IAssistDataReceiver;
@@ -55,7 +56,7 @@
* Called when the {@link android.app.assist.AssistContent} of the requested task is
* available.
**/
- void onAssistContentAvailable(AssistContent assistContent);
+ void onAssistContentAvailable(@Nullable AssistContent assistContent);
}
private final IActivityTaskManager mActivityTaskManager;
@@ -117,15 +118,9 @@
@Override
public void onHandleAssistData(Bundle data) {
- if (data == null) {
- return;
- }
-
- final AssistContent content = data.getParcelable(ASSIST_KEY_CONTENT);
- if (content == null) {
- Log.e(TAG, "Received AssistData, but no AssistContent found");
- return;
- }
+ final AssistContent content = (data == null) ? null
+ : data.getParcelable(
+ ASSIST_KEY_CONTENT, AssistContent.class);
AssistContentRequester requester = mParentRef.get();
if (requester != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
index 6224e1b..afb0280 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.screenshot;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -29,4 +30,9 @@
static ScreenshotNotificationSmartActionsProvider providesScrnshtNotifSmartActionsProvider() {
return new ScreenshotNotificationSmartActionsProvider();
}
+
+ /** */
+ @Binds
+ ScreenshotActionsProvider.Factory bindScreenshotActionsProviderFactory(
+ DefaultScreenshotActionsProvider.Factory defaultScreenshotActionsProviderFactory);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
index 0ccb19c..07e143a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
@@ -28,7 +28,6 @@
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_EDIT_TAPPED
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SHARE_TAPPED
-import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED
import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import dagger.assisted.Assisted
@@ -43,7 +42,11 @@
fun onScrollChipReady(onClick: Runnable)
fun setCompletedScreenshot(result: ScreenshotSavedResult)
- fun onAssistContentAvailable(assistContent: AssistContent) {}
+ /**
+ * Provide the AssistContent for the focused task if available, null if the focused task isn't
+ * known or didn't return data.
+ */
+ fun onAssistContent(assistContent: AssistContent?) {}
interface Factory {
fun create(
@@ -59,7 +62,6 @@
constructor(
private val context: Context,
private val viewModel: ScreenshotViewModel,
- private val smartActionsProvider: SmartActionsProvider,
private val uiEventLogger: UiEventLogger,
@Assisted val request: ScreenshotData,
@Assisted val requestId: String,
@@ -115,37 +117,6 @@
)
}
}
-
- smartActionsProvider.requestQuickShare(request, requestId) { quickShare ->
- if (!quickShare.actionIntent.isImmutable) {
- viewModel.addAction(
- ActionButtonAppearance(
- quickShare.getIcon().loadDrawable(context),
- quickShare.title,
- quickShare.title
- )
- ) {
- debugLog(LogConfig.DEBUG_ACTIONS) { "Quickshare tapped" }
- onDeferrableActionTapped { result ->
- uiEventLogger.log(
- SCREENSHOT_SMART_ACTION_TAPPED,
- 0,
- request.packageNameString
- )
- val pendingIntentWithUri =
- smartActionsProvider.wrapIntent(
- quickShare,
- result.uri,
- result.subject,
- requestId
- )
- actionExecutor.sendPendingIntent(pendingIntentWithUri)
- }
- }
- } else {
- Log.w(TAG, "Received immutable quick share pending intent; ignoring")
- }
- }
}
override fun onScrollChipReady(onClick: Runnable) {
@@ -167,21 +138,6 @@
}
this.result = result
pendingAction?.invoke(result)
- smartActionsProvider.requestSmartActions(request, requestId, result) { smartActions ->
- smartActions.forEach {
- smartActions.forEach { action ->
- viewModel.addAction(
- ActionButtonAppearance(
- action.getIcon().loadDrawable(context),
- action.title,
- action.title,
- )
- ) {
- actionExecutor.sendPendingIntent(action.actionIntent)
- }
- }
- }
- }
}
private fun onDeferrableActionTapped(onResult: (ScreenshotSavedResult) -> Unit) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 13dd229..6871084 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -176,11 +176,12 @@
// These strings are used for communicating the action invoked to
// ScreenshotNotificationSmartActionsProvider.
- static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
- static final String EXTRA_ID = "android:screenshot_id";
- static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
- static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
- static final String EXTRA_ACTION_INTENT_FILLIN = "android:screenshot_action_intent_fillin";
+ public static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
+ public static final String EXTRA_ID = "android:screenshot_id";
+ public static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
+ public static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
+ public static final String EXTRA_ACTION_INTENT_FILLIN =
+ "android:screenshot_action_intent_fillin";
// From WizardManagerHelper.java
@@ -411,9 +412,9 @@
if (screenshot.getTaskId() >= 0) {
mAssistContentRequester.requestAssistContent(screenshot.getTaskId(),
- assistContent -> {
- mActionsProvider.onAssistContentAvailable(assistContent);
- });
+ assistContent -> mActionsProvider.onAssistContent(assistContent));
+ } else {
+ mActionsProvider.onAssistContent(null);
}
} else {
saveScreenshotInWorkerThread(screenshot.getUserHandle(), finisher,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
index 3eafbfb..23f05e0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
@@ -44,7 +44,7 @@
public static final String DEFAULT_ACTION_TYPE = "Smart Action";
/* Define phases of screenshot execution. */
- protected enum ScreenshotOp {
+ public enum ScreenshotOp {
OP_UNKNOWN,
RETRIEVE_SMART_ACTIONS,
REQUEST_SMART_ACTIONS,
@@ -52,7 +52,7 @@
}
/* Enum to report success or failure for screenshot execution phases. */
- protected enum ScreenshotOpStatus {
+ public enum ScreenshotOpStatus {
OP_STATUS_UNKNOWN,
SUCCESS,
ERROR,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
index 6b9332b..254c133 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
@@ -28,6 +28,7 @@
import android.view.View
import android.view.ViewTreeObserver
import android.view.WindowInsets
+import android.view.WindowManager
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import com.android.internal.logging.UiEventLogger
@@ -53,6 +54,7 @@
constructor(
private val logger: UiEventLogger,
private val viewModel: ScreenshotViewModel,
+ private val windowManager: WindowManager,
@Assisted private val context: Context,
@Assisted private val displayId: Int
) : ScreenshotViewProxy {
@@ -79,6 +81,16 @@
addPredictiveBackListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
setOnKeyListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
debugLog(DEBUG_WINDOW) { "adding OnComputeInternalInsetsListener" }
+ view.viewTreeObserver.addOnComputeInternalInsetsListener { info ->
+ val touchableRegion =
+ view.getTouchRegion(
+ windowManager.currentWindowMetrics.windowInsets.getInsets(
+ WindowInsets.Type.systemGestures()
+ )
+ )
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION)
+ info.touchableRegion.set(touchableRegion)
+ }
screenshotPreview = view.screenshotPreview
}
@@ -194,6 +206,7 @@
}
)
}
+
private fun setOnKeyListener(onDismissRequested: (ScreenshotEvent) -> Unit) {
view.setOnKeyListener(
object : View.OnKeyListener {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsProvider.kt
deleted file mode 100644
index a895b30..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsProvider.kt
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot
-
-import android.app.Notification
-import android.app.PendingIntent
-import android.content.ClipData
-import android.content.ClipDescription
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.graphics.Bitmap
-import android.net.Uri
-import android.os.Bundle
-import android.os.Process
-import android.os.SystemClock
-import android.os.UserHandle
-import android.provider.DeviceConfig
-import android.util.Log
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
-import com.android.systemui.log.DebugLogger.debugLog
-import com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS
-import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType.QUICK_SHARE_ACTION
-import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType.REGULAR_SMART_ACTIONS
-import java.util.concurrent.CompletableFuture
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.TimeoutException
-import javax.inject.Inject
-import kotlin.random.Random
-
-/**
- * Handle requesting smart/quickshare actions from the provider and executing an action when the
- * action futures complete.
- */
-class SmartActionsProvider
-@Inject
-constructor(
- private val context: Context,
- private val smartActions: ScreenshotNotificationSmartActionsProvider,
-) {
- /**
- * Requests quick share action for a given screenshot.
- *
- * @param data the ScreenshotData request
- * @param id the request id for the screenshot
- * @param onAction callback to run when quick share action is returned
- */
- fun requestQuickShare(
- data: ScreenshotData,
- id: String,
- onAction: (Notification.Action) -> Unit
- ) {
- val bitmap = data.bitmap ?: return
- val component = data.topComponent ?: ComponentName("", "")
- requestQuickShareAction(id, bitmap, component, data.getUserOrDefault()) { quickShare ->
- onAction(quickShare)
- }
- }
-
- /**
- * Requests smart actions for a given screenshot.
- *
- * @param data the ScreenshotData request
- * @param id the request id for the screenshot
- * @param result the data for the saved image
- * @param onActions callback to run when actions are returned
- */
- fun requestSmartActions(
- data: ScreenshotData,
- id: String,
- result: ScreenshotSavedResult,
- onActions: (List<Notification.Action>) -> Unit
- ) {
- val bitmap = data.bitmap ?: return
- val component = data.topComponent ?: ComponentName("", "")
- requestSmartActions(
- id,
- bitmap,
- component,
- data.getUserOrDefault(),
- result.uri,
- REGULAR_SMART_ACTIONS
- ) { actions ->
- onActions(actions)
- }
- }
-
- /**
- * Wraps the given quick share action in a broadcast intent.
- *
- * @param quickShare the quick share action to wrap
- * @param uri the URI of the saved screenshot
- * @param subject the subject/title for the screenshot
- * @param id the request ID of the screenshot
- * @return the pending intent with correct URI
- */
- fun wrapIntent(
- quickShare: Notification.Action,
- uri: Uri,
- subject: String,
- id: String
- ): PendingIntent {
- val wrappedIntent: Intent =
- Intent(context, SmartActionsReceiver::class.java)
- .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, quickShare.actionIntent)
- .putExtra(
- ScreenshotController.EXTRA_ACTION_INTENT_FILLIN,
- createFillInIntent(uri, subject)
- )
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- val extras: Bundle = quickShare.extras
- val actionType =
- extras.getString(
- ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
- ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE
- )
- // We only query for quick share actions when smart actions are enabled, so we can assert
- // that it's true here.
- wrappedIntent
- .putExtra(ScreenshotController.EXTRA_ACTION_TYPE, actionType)
- .putExtra(ScreenshotController.EXTRA_ID, id)
- .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, true)
- return PendingIntent.getBroadcast(
- context,
- Random.nextInt(),
- wrappedIntent,
- PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
- }
-
- private fun createFillInIntent(uri: Uri, subject: String): Intent {
- val fillIn = Intent()
- fillIn.setType("image/png")
- fillIn.putExtra(Intent.EXTRA_STREAM, uri)
- fillIn.putExtra(Intent.EXTRA_SUBJECT, subject)
- // Include URI in ClipData also, so that grantPermission picks it up.
- // We don't use setData here because some apps interpret this as "to:".
- val clipData =
- ClipData(ClipDescription("content", arrayOf("image/png")), ClipData.Item(uri))
- fillIn.clipData = clipData
- fillIn.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- return fillIn
- }
-
- private fun requestQuickShareAction(
- id: String,
- image: Bitmap,
- component: ComponentName,
- user: UserHandle,
- timeoutMs: Long = 500,
- onAction: (Notification.Action) -> Unit
- ) {
- requestSmartActions(id, image, component, user, null, QUICK_SHARE_ACTION, timeoutMs) {
- it.firstOrNull()?.let { action -> onAction(action) }
- }
- }
-
- private fun requestSmartActions(
- id: String,
- image: Bitmap,
- component: ComponentName,
- user: UserHandle,
- uri: Uri?,
- actionType: ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType,
- timeoutMs: Long = 500,
- onActions: (List<Notification.Action>) -> Unit
- ) {
- val enabled = isSmartActionsEnabled(user)
- debugLog(DEBUG_ACTIONS) {
- ("getSmartActionsFuture id=$id, uri=$uri, provider=$smartActions, " +
- "actionType=$actionType, smartActionsEnabled=$enabled, userHandle=$user")
- }
- if (!enabled) {
- debugLog(DEBUG_ACTIONS) { "Screenshot Intelligence not enabled, returning empty list" }
- onActions(listOf())
- return
- }
- if (image.config != Bitmap.Config.HARDWARE) {
- debugLog(DEBUG_ACTIONS) {
- "Bitmap expected: Hardware, Bitmap found: ${image.config}. Returning empty list."
- }
- onActions(listOf())
- return
- }
- val smartActionsFuture: CompletableFuture<List<Notification.Action>>
- val startTimeMs = SystemClock.uptimeMillis()
- try {
- smartActionsFuture =
- smartActions.getActions(id, uri, image, component, actionType, user)
- } catch (e: Throwable) {
- val waitTimeMs = SystemClock.uptimeMillis() - startTimeMs
- debugLog(DEBUG_ACTIONS, error = e) {
- "Failed to get future for screenshot notification smart actions."
- }
- notifyScreenshotOp(
- id,
- ScreenshotNotificationSmartActionsProvider.ScreenshotOp.REQUEST_SMART_ACTIONS,
- ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR,
- waitTimeMs
- )
- onActions(listOf())
- return
- }
- try {
- val actions = smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)
- val waitTimeMs = SystemClock.uptimeMillis() - startTimeMs
- debugLog(DEBUG_ACTIONS) {
- ("Got ${actions.size} smart actions. Wait time: $waitTimeMs ms, " +
- "actionType=$actionType")
- }
- notifyScreenshotOp(
- id,
- ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
- ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.SUCCESS,
- waitTimeMs
- )
- onActions(actions)
- } catch (e: Throwable) {
- val waitTimeMs = SystemClock.uptimeMillis() - startTimeMs
- debugLog(DEBUG_ACTIONS, error = e) {
- "Error getting smart actions. Wait time: $waitTimeMs ms, actionType=$actionType"
- }
- val status =
- if (e is TimeoutException) {
- ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.TIMEOUT
- } else {
- ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR
- }
- notifyScreenshotOp(
- id,
- ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
- status,
- waitTimeMs
- )
- onActions(listOf())
- }
- }
-
- private fun notifyScreenshotOp(
- screenshotId: String,
- op: ScreenshotNotificationSmartActionsProvider.ScreenshotOp,
- status: ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus,
- durationMs: Long
- ) {
- debugLog(DEBUG_ACTIONS) {
- "$smartActions notifyOp: $op id=$screenshotId, status=$status, durationMs=$durationMs"
- }
- try {
- smartActions.notifyOp(screenshotId, op, status, durationMs)
- } catch (e: Throwable) {
- Log.e(TAG, "Error in notifyScreenshotOp: ", e)
- }
- }
-
- private fun isSmartActionsEnabled(user: UserHandle): Boolean {
- // Smart actions don't yet work for cross-user saves.
- val savingToOtherUser = user !== Process.myUserHandle()
- val actionsEnabled =
- DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS,
- true
- )
- return !savingToOtherUser && actionsEnabled
- }
-
- companion object {
- private const val TAG = "SmartActionsProvider"
- private const val SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)"
- }
-}
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 6ff0fda..ab23e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -22,11 +22,9 @@
import android.view.accessibility.AccessibilityManager;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.screenshot.DefaultScreenshotActionsProvider;
import com.android.systemui.screenshot.ImageCapture;
import com.android.systemui.screenshot.ImageCaptureImpl;
import com.android.systemui.screenshot.LegacyScreenshotViewProxy;
-import com.android.systemui.screenshot.ScreenshotActionsProvider;
import com.android.systemui.screenshot.ScreenshotPolicy;
import com.android.systemui.screenshot.ScreenshotPolicyImpl;
import com.android.systemui.screenshot.ScreenshotShelfViewProxy;
@@ -90,10 +88,6 @@
abstract ScreenshotSoundController bindScreenshotSoundController(
ScreenshotSoundControllerImpl screenshotSoundProviderImpl);
- @Binds
- abstract ScreenshotActionsProvider.Factory bindScreenshotActionsProviderFactory(
- DefaultScreenshotActionsProvider.Factory defaultScreenshotActionsProviderFactory);
-
@Provides
@SysUISingleton
static ScreenshotViewModel providesScreenshotViewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt
index 4a88180..0fb5366 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt
@@ -22,7 +22,28 @@
fun interface CapturePolicy {
/**
* Test the policy against the current display task state. If the policy applies, Returns a
- * non-null [CaptureParameters] describing how the screenshot request should be augmented.
+ * [PolicyResult.Matched] containing [CaptureParameters] used to alter the request.
*/
- suspend fun apply(content: DisplayContentModel): CaptureParameters?
+ suspend fun check(content: DisplayContentModel): PolicyResult
+
+ /** The result of a screen capture policy check. */
+ sealed interface PolicyResult {
+ /** The policy rules matched the given display content and will be applied. */
+ data class Matched(
+ /** The name of the policy rule which matched. */
+ val policy: String,
+ /** Why the policy matched. */
+ val reason: String,
+ /** Details on how to modify the screen capture request. */
+ val parameters: CaptureParameters,
+ ) : PolicyResult
+
+ /** The policy rules do not match the given display content and do not apply. */
+ data class NotMatched(
+ /** The name of the policy rule which matched. */
+ val policy: String,
+ /** Why the policy did not match. */
+ val reason: String
+ ) : PolicyResult
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt
index 6ca2e9d6..0ef5207 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt
@@ -21,10 +21,10 @@
/** What to capture */
sealed interface CaptureType {
/** Capture the entire screen contents. */
- class FullScreen(val displayId: Int) : CaptureType
+ data class FullScreen(val displayId: Int) : CaptureType
/** Capture the contents of the task only. */
- class IsolatedTask(
+ data class IsolatedTask(
val taskId: Int,
val taskBounds: Rect?,
) : CaptureType
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
index 2c0a0db..80aa0ef 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
@@ -16,9 +16,12 @@
package com.android.systemui.screenshot.policy
+import android.app.ActivityTaskManager.RootTaskInfo
+import android.app.WindowConfiguration
import android.content.ComponentName
import android.graphics.Bitmap
import android.graphics.Rect
+import android.os.Process.myUserHandle
import android.os.UserHandle
import android.util.Log
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
@@ -27,7 +30,10 @@
import com.android.systemui.screenshot.ImageCapture
import com.android.systemui.screenshot.ScreenshotData
import com.android.systemui.screenshot.ScreenshotRequestProcessor
+import com.android.systemui.screenshot.data.model.DisplayContentModel
import com.android.systemui.screenshot.data.repository.DisplayContentRepository
+import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.Matched
+import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.NotMatched
import com.android.systemui.screenshot.policy.CaptureType.FullScreen
import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask
import kotlinx.coroutines.CoroutineDispatcher
@@ -39,8 +45,14 @@
class PolicyRequestProcessor(
@Background private val background: CoroutineDispatcher,
private val capture: ImageCapture,
+ /** Provides information about the tasks on a given display */
private val displayTasks: DisplayContentRepository,
+ /** The list of policies to apply, in order of priority */
private val policies: List<CapturePolicy>,
+ /** The owner to assign for screenshot when a focused task isn't visible */
+ private val defaultOwner: UserHandle = myUserHandle(),
+ /** The assigned component when no application has focus, or not visible */
+ private val defaultComponent: ComponentName,
) : ScreenshotRequestProcessor {
override suspend fun process(original: ScreenshotData): ScreenshotData {
if (original.type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
@@ -48,29 +60,26 @@
Log.i(TAG, "Screenshot bitmap provided. No modifications applied.")
return original
}
-
- val tasks = displayTasks.getDisplayContent(original.displayId)
+ val displayContent = displayTasks.getDisplayContent(original.displayId)
// If policies yield explicit modifications, apply them and return the result
Log.i(TAG, "Applying policy checks....")
- policies
- .firstNotNullOfOrNull { policy -> policy.apply(tasks) }
- ?.let {
- Log.i(TAG, "Modifying screenshot: $it")
- return apply(it, original)
+ policies.map { policy ->
+ when (val result = policy.check(displayContent)) {
+ is Matched -> {
+ Log.i(TAG, "$result")
+ return modify(original, result.parameters)
+ }
+ is NotMatched -> Log.i(TAG, "$result")
}
+ }
// Otherwise capture normally, filling in additional information as needed.
- return replaceWithScreenshot(
- original = original,
- componentName = original.topComponent ?: tasks.rootTasks.firstOrNull()?.topActivity,
- owner = original.userHandle,
- displayId = original.displayId
- )
+ return captureScreenshot(original, displayContent)
}
/** Produce a new [ScreenshotData] using [CaptureParameters] */
- suspend fun apply(updates: CaptureParameters, original: ScreenshotData): ScreenshotData {
+ suspend fun modify(original: ScreenshotData, updates: CaptureParameters): ScreenshotData {
// Update and apply bitmap capture depending on the parameters.
val updated =
when (val type = updates.type) {
@@ -93,6 +102,26 @@
return updated
}
+ private suspend fun captureScreenshot(
+ original: ScreenshotData,
+ displayContent: DisplayContentModel,
+ ): ScreenshotData {
+ // The first root task on the display, excluding Picture-in-Picture
+ val topMainRootTask =
+ if (!displayContent.systemUiState.shadeExpanded) {
+ displayContent.rootTasks.firstOrNull(::nonPipVisibleTask)
+ } else {
+ null // Otherwise attributed to SystemUI / current user
+ }
+
+ return replaceWithScreenshot(
+ original = original,
+ componentName = topMainRootTask?.topActivity ?: defaultComponent,
+ owner = topMainRootTask?.userId?.let { UserHandle.of(it) } ?: defaultOwner,
+ displayId = original.displayId
+ )
+ }
+
suspend fun replaceWithTaskSnapshot(
original: ScreenshotData,
componentName: ComponentName?,
@@ -100,6 +129,7 @@
taskId: Int,
taskBounds: Rect?,
): ScreenshotData {
+ Log.i(TAG, "Capturing task snapshot: $componentName / $owner")
val taskSnapshot = capture.captureTask(taskId)
return original.copy(
type = TAKE_SCREENSHOT_PROVIDED_IMAGE,
@@ -117,6 +147,7 @@
owner: UserHandle?,
displayId: Int,
): ScreenshotData {
+ Log.i(TAG, "Capturing screenshot: $componentName / $owner")
val screenshot = captureDisplay(displayId)
return original.copy(
type = TAKE_SCREENSHOT_FULLSCREEN,
@@ -127,6 +158,16 @@
)
}
+ /** Filter for the task used to attribute a full screen capture to an owner */
+ private fun nonPipVisibleTask(info: RootTaskInfo): Boolean {
+ return info.windowingMode != WindowConfiguration.WINDOWING_MODE_PINNED &&
+ info.isVisible &&
+ info.isRunning &&
+ info.numActivities > 0 &&
+ info.topActivity != null &&
+ info.childTaskIds.isNotEmpty()
+ }
+
/** TODO: Move to ImageCapture (existing function is non-suspending) */
private suspend fun captureDisplay(displayId: Int): Bitmap? {
return withContext(background) { capture.captureDisplay(displayId) }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt
index 221e647..1945c25 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt
@@ -20,10 +20,11 @@
import com.android.systemui.screenshot.data.model.DisplayContentModel
import com.android.systemui.screenshot.data.model.ProfileType
import com.android.systemui.screenshot.data.repository.ProfileTypeRepository
+import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult
+import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.Matched
+import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.NotMatched
import com.android.systemui.screenshot.policy.CaptureType.FullScreen
import javax.inject.Inject
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.firstOrNull
/**
* Condition: When any visible task belongs to a private user.
@@ -35,9 +36,14 @@
constructor(
private val profileTypes: ProfileTypeRepository,
) : CapturePolicy {
- override suspend fun apply(content: DisplayContentModel): CaptureParameters? {
+ override suspend fun check(content: DisplayContentModel): PolicyResult {
+ // The systemUI notification shade isn't a private profile app, skip.
+ if (content.systemUiState.shadeExpanded) {
+ return NotMatched(policy = NAME, reason = SHADE_EXPANDED)
+ }
+
// Find the first visible rootTaskInfo with a child task owned by a private user
- val (rootTask, childTask) =
+ val childTask =
content.rootTasks
.filter { it.isVisible }
.firstNotNullOfOrNull { root ->
@@ -46,15 +52,24 @@
.firstOrNull {
profileTypes.getProfileType(it.userId) == ProfileType.PRIVATE
}
- ?.let { root to it }
}
- ?: return null
+ ?: return NotMatched(policy = NAME, reason = NO_VISIBLE_TASKS)
// If matched, return parameters needed to modify the request.
- return CaptureParameters(
- type = FullScreen(content.displayId),
- component = childTask.componentName ?: rootTask.topActivity,
- owner = UserHandle.of(childTask.userId),
+ return Matched(
+ policy = NAME,
+ reason = PRIVATE_TASK_VISIBLE,
+ CaptureParameters(
+ type = FullScreen(content.displayId),
+ component = content.rootTasks.first { it.isVisible }.topActivity,
+ owner = UserHandle.of(childTask.userId),
+ )
)
}
+ companion object {
+ const val NAME = "PrivateProfile"
+ const val SHADE_EXPANDED = "Notification shade is expanded"
+ const val NO_VISIBLE_TASKS = "No private profile tasks are visible"
+ const val PRIVATE_TASK_VISIBLE = "At least one private profile task is visible"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt
index d2f4d9e..f768cfb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt
@@ -18,12 +18,10 @@
import android.app.ActivityTaskManager.RootTaskInfo
import com.android.systemui.screenshot.data.model.ChildTaskModel
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.asFlow
-import kotlinx.coroutines.flow.map
-internal fun RootTaskInfo.childTasksTopDown(): Flow<ChildTaskModel> {
- return ((numActivities - 1) downTo 0).asFlow().map { index ->
+/** The child tasks of A RootTaskInfo as [ChildTaskModel] in top-down (z-index ascending) order. */
+internal fun RootTaskInfo.childTasksTopDown(): Sequence<ChildTaskModel> {
+ return ((childTaskIds.size - 1) downTo 0).asSequence().map { index ->
ChildTaskModel(
childTaskIds[index],
childTaskNames[index],
@@ -33,15 +31,4 @@
}
}
-internal suspend fun RootTaskInfo.firstChildTaskOrNull(
- filter: suspend (Int) -> Boolean
-): Pair<RootTaskInfo, Int>? {
- // Child tasks are provided in bottom-up order
- // Filtering is done top-down, so iterate backwards here.
- for (index in numActivities - 1 downTo 0) {
- if (filter(index)) {
- return (this to index)
- }
- }
- return null
-}
+internal fun RootTaskInfo.hasChildTasks() = childTaskUserIds.isNotEmpty()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
index 63d1508..44f767a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
@@ -16,8 +16,13 @@
package com.android.systemui.screenshot.policy
-import com.android.systemui.Flags.screenshotPrivateProfile
+import android.content.ComponentName
+import android.content.Context
+import android.os.Process
+import com.android.systemui.Flags.screenshotPrivateProfileBehaviorFix
+import com.android.systemui.SystemUIService
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.screenshot.ImageCapture
import com.android.systemui.screenshot.RequestProcessor
@@ -60,18 +65,22 @@
@Provides
@SysUISingleton
fun bindScreenshotRequestProcessor(
+ @Application context: Context,
@Background background: CoroutineDispatcher,
imageCapture: ImageCapture,
policyProvider: Provider<ScreenshotPolicy>,
displayContentRepoProvider: Provider<DisplayContentRepository>,
policyListProvider: Provider<List<CapturePolicy>>,
): ScreenshotRequestProcessor {
- return if (screenshotPrivateProfile()) {
+ return if (screenshotPrivateProfileBehaviorFix()) {
PolicyRequestProcessor(
- background,
- imageCapture,
- displayContentRepoProvider.get(),
- policyListProvider.get()
+ background = background,
+ capture = imageCapture,
+ displayTasks = displayContentRepoProvider.get(),
+ policies = policyListProvider.get(),
+ defaultOwner = Process.myUserHandle(),
+ defaultComponent =
+ ComponentName(context.packageName, SystemUIService::class.java.toString())
)
} else {
RequestProcessor(imageCapture, policyProvider.get())
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
index d6b5d6d..fdf16aa 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
@@ -16,12 +16,16 @@
package com.android.systemui.screenshot.policy
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
import android.os.UserHandle
import com.android.systemui.screenshot.data.model.DisplayContentModel
import com.android.systemui.screenshot.data.model.ProfileType
import com.android.systemui.screenshot.data.repository.ProfileTypeRepository
+import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult
+import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.NotMatched
import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask
+import com.android.window.flags.Flags
import javax.inject.Inject
import kotlinx.coroutines.flow.first
@@ -35,22 +39,56 @@
constructor(
private val profileTypes: ProfileTypeRepository,
) : CapturePolicy {
- override suspend fun apply(content: DisplayContentModel): CaptureParameters? {
+
+ override suspend fun check(content: DisplayContentModel): PolicyResult {
+ // The systemUI notification shade isn't a work app, skip.
+ if (content.systemUiState.shadeExpanded) {
+ return NotMatched(policy = NAME, reason = SHADE_EXPANDED)
+ }
+
+ if (Flags.enableDesktopWindowingMode()) {
+ content.rootTasks.firstOrNull()?.also {
+ if (it.windowingMode == WINDOWING_MODE_FREEFORM) {
+ return NotMatched(policy = NAME, reason = DESKTOP_MODE_ENABLED)
+ }
+ }
+ }
+
// Find the first non PiP rootTask with a top child task owned by a work user
val (rootTask, childTask) =
content.rootTasks
- .filter { it.isVisible && it.windowingMode != WINDOWING_MODE_PINNED }
+ .filter {
+ it.isVisible && it.windowingMode != WINDOWING_MODE_PINNED && it.hasChildTasks()
+ }
.map { it to it.childTasksTopDown().first() }
.firstOrNull { (_, child) ->
profileTypes.getProfileType(child.userId) == ProfileType.WORK
}
- ?: return null
+ ?: return NotMatched(
+ policy = NAME,
+ reason = WORK_TASK_NOT_TOP,
+ )
// If matched, return parameters needed to modify the request.
- return CaptureParameters(
- type = IsolatedTask(taskId = childTask.id, taskBounds = childTask.bounds),
- component = childTask.componentName ?: rootTask.topActivity,
- owner = UserHandle.of(childTask.userId),
+ return PolicyResult.Matched(
+ policy = NAME,
+ reason = WORK_TASK_IS_TOP,
+ CaptureParameters(
+ type = IsolatedTask(taskId = childTask.id, taskBounds = childTask.bounds),
+ component = childTask.componentName ?: rootTask.topActivity,
+ owner = UserHandle.of(childTask.userId),
+ )
)
}
+
+ companion object {
+ const val NAME = "WorkProfile"
+ const val SHADE_EXPANDED = "Notification shade is expanded"
+ const val WORK_TASK_NOT_TOP =
+ "The top-most non-PINNED task does not belong to a work profile user"
+ const val WORK_TASK_IS_TOP = "The top-most non-PINNED task belongs to a work profile user"
+ const val DESKTOP_MODE_ENABLED =
+ "enable_desktop_windowing_mode is enabled and top " +
+ "RootTask has WINDOWING_MODE_FREEFORM"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
index 706ac9c..b43a1d2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.HardwareRenderer;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
@@ -38,9 +39,11 @@
import android.view.Display;
import android.view.ScrollCaptureResponse;
import android.view.View;
+import android.view.WindowInsets;
import android.widget.ImageView;
import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.core.view.WindowCompat;
import com.android.internal.app.ChooserActivity;
import com.android.internal.logging.UiEventLogger;
@@ -127,6 +130,10 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ // Enable edge-to-edge explicitly.
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+
setContentView(R.layout.long_screenshot);
mPreview = requireViewById(R.id.preview);
@@ -149,6 +156,13 @@
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
updateImageDimensions());
+ requireViewById(R.id.root).setOnApplyWindowInsetsListener(
+ (view, windowInsets) -> {
+ Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars());
+ view.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ return WindowInsets.CONSUMED;
+ });
+
Intent intent = getIntent();
mScrollCaptureResponse = intent.getParcelableExtra(EXTRA_CAPTURE_RESPONSE);
mScreenshotUserHandle = intent.getParcelableExtra(EXTRA_SCREENSHOT_USER_HANDLE,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
index 747ad4f..b7a03ef 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
@@ -17,17 +17,70 @@
package com.android.systemui.screenshot.ui
import android.content.Context
+import android.graphics.Insets
+import android.graphics.Rect
+import android.graphics.Region
import android.util.AttributeSet
+import android.view.View
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import com.android.systemui.res.R
+import com.android.systemui.screenshot.FloatingWindowUtil
class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) :
ConstraintLayout(context, attrs) {
lateinit var screenshotPreview: ImageView
+ private val displayMetrics = context.resources.displayMetrics
+ private val tmpRect = Rect()
+ private lateinit var actionsContainerBackground: View
+ private lateinit var dismissButton: View
+
override fun onFinishInflate() {
super.onFinishInflate()
screenshotPreview = requireViewById(R.id.screenshot_preview)
+ actionsContainerBackground = requireViewById(R.id.actions_container_background)
+ dismissButton = requireViewById(R.id.screenshot_dismiss_button)
+ }
+
+ fun getTouchRegion(gestureInsets: Insets): Region {
+ val region = getSwipeRegion()
+
+ // Receive touches in gesture insets so they don't cause TOUCH_OUTSIDE
+ // left edge gesture region
+ val insetRect = Rect(0, 0, gestureInsets.left, displayMetrics.heightPixels)
+ region.op(insetRect, Region.Op.UNION)
+ // right edge gesture region
+ insetRect.set(
+ displayMetrics.widthPixels - gestureInsets.right,
+ 0,
+ displayMetrics.widthPixels,
+ displayMetrics.heightPixels
+ )
+ region.op(insetRect, Region.Op.UNION)
+
+ return region
+ }
+
+ private fun getSwipeRegion(): Region {
+ val swipeRegion = Region()
+ val padding = FloatingWindowUtil.dpToPx(displayMetrics, -1 * TOUCH_PADDING_DP).toInt()
+ swipeRegion.addInsetView(screenshotPreview, padding)
+ swipeRegion.addInsetView(actionsContainerBackground, padding)
+ swipeRegion.addInsetView(dismissButton, padding)
+ findViewById<View>(R.id.screenshot_message_container)?.let {
+ swipeRegion.addInsetView(it, padding)
+ }
+ return swipeRegion
+ }
+
+ private fun Region.addInsetView(view: View, padding: Int = 0) {
+ view.getBoundsOnScreen(tmpRect)
+ tmpRect.inset(padding, padding)
+ this.op(tmpRect, Region.Op.UNION)
+ }
+
+ companion object {
+ private const val TOUCH_PADDING_DP = 12f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
index 32e9296..5f835b3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
@@ -65,7 +65,9 @@
}
launch {
viewModel.actions.collect { actions ->
- if (actions.isNotEmpty()) {
+ val visibleActions = actions.filter { it.visible }
+
+ if (visibleActions.isNotEmpty()) {
view
.requireViewById<View>(R.id.actions_container_background)
.visibility = View.VISIBLE
@@ -75,7 +77,7 @@
// any new actions and update any that are already there.
// This assumes that actions can never change order and that each action
// ID is unique.
- val newIds = actions.map { it.id }
+ val newIds = visibleActions.map { it.id }
for (view in actionsContainer.children.toList()) {
if (view.tag !in newIds) {
@@ -83,7 +85,7 @@
}
}
- for ((index, action) in actions.withIndex()) {
+ for ((index, action) in visibleActions.withIndex()) {
val currentView: View? = actionsContainer.getChildAt(index)
if (action.id == currentView?.tag) {
// Same ID, update the display
@@ -93,7 +95,7 @@
// mean that the new action must be inserted here.
val actionButton =
layoutInflater.inflate(
- R.layout.overlay_action_chip,
+ R.layout.shelf_action_chip,
actionsContainer,
false
)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt
index 64b0105..c5fa8db 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt
@@ -19,6 +19,7 @@
data class ActionButtonViewModel(
val appearance: ActionButtonAppearance,
val id: Int,
+ val visible: Boolean,
val onClicked: (() -> Unit)?,
) {
companion object {
@@ -29,6 +30,6 @@
fun withNextId(
appearance: ActionButtonAppearance,
onClicked: (() -> Unit)?
- ): ActionButtonViewModel = ActionButtonViewModel(appearance, getId(), onClicked)
+ ): ActionButtonViewModel = ActionButtonViewModel(appearance, getId(), true, onClicked)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
index fa34803..f67ad40 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
@@ -48,12 +48,34 @@
return action.id
}
+ fun setActionVisibility(actionId: Int, visible: Boolean) {
+ val actionList = _actions.value.toMutableList()
+ val index = actionList.indexOfFirst { it.id == actionId }
+ if (index >= 0) {
+ actionList[index] =
+ ActionButtonViewModel(
+ actionList[index].appearance,
+ actionId,
+ visible,
+ actionList[index].onClicked
+ )
+ _actions.value = actionList
+ } else {
+ Log.w(TAG, "Attempted to update unknown action id $actionId")
+ }
+ }
+
fun updateActionAppearance(actionId: Int, appearance: ActionButtonAppearance) {
val actionList = _actions.value.toMutableList()
val index = actionList.indexOfFirst { it.id == actionId }
if (index >= 0) {
actionList[index] =
- ActionButtonViewModel(appearance, actionId, actionList[index].onClicked)
+ ActionButtonViewModel(
+ appearance,
+ actionId,
+ actionList[index].visible,
+ actionList[index].onClicked
+ )
_actions.value = actionList
} else {
Log.w(TAG, "Attempted to update unknown action id $actionId")
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index f6b1bcc..8f6b9d0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -23,16 +23,24 @@
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
+import androidx.activity.OnBackPressedDispatcher
+import androidx.activity.OnBackPressedDispatcherOwner
+import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.compose.ui.platform.ComposeView
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.theme.PlatformTheme
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.communal.dagger.Communal
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.ui.compose.CommunalContainer
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.util.CommunalColors
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.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -40,6 +48,7 @@
import com.android.systemui.util.kotlin.collectFlow
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
/**
* Controller that's responsible for the glanceable hub container view and its touch handling.
@@ -56,6 +65,7 @@
private val keyguardInteractor: KeyguardInteractor,
private val shadeInteractor: ShadeInteractor,
private val powerManager: PowerManager,
+ private val communalColors: CommunalColors,
@Communal private val dataSourceDelegator: SceneDataSourceDelegator,
) {
/** The container view for the hub. This will not be initialized until [initView] is called. */
@@ -139,13 +149,34 @@
): View {
return initView(
ComposeView(context).apply {
- setContent {
- PlatformTheme {
- CommunalContainer(
- viewModel = communalViewModel,
- dataSourceDelegator = dataSourceDelegator,
- dialogFactory = dialogFactory,
- )
+ repeatWhenAttached {
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ setViewTreeOnBackPressedDispatcherOwner(
+ object : OnBackPressedDispatcherOwner {
+ override val onBackPressedDispatcher =
+ OnBackPressedDispatcher().apply {
+ setOnBackInvokedDispatcher(
+ viewRootImpl.onBackInvokedDispatcher
+ )
+ }
+
+ override val lifecycle: Lifecycle =
+ this@repeatWhenAttached.lifecycle
+ }
+ )
+
+ setContent {
+ PlatformTheme {
+ CommunalContainer(
+ viewModel = communalViewModel,
+ colors = communalColors,
+ dataSourceDelegator = dataSourceDelegator,
+ dialogFactory = dialogFactory,
+ )
+ }
+ }
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 4660831..5d0b77f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -30,6 +30,11 @@
import static com.android.systemui.classifier.Classifier.GENERIC;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.UNLOCK;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.GONE;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
@@ -186,6 +191,7 @@
import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -433,6 +439,7 @@
private boolean mExpandingFromHeadsUp;
private boolean mCollapsedOnDown;
private boolean mClosingWithAlphaFadeOut;
+ private boolean mHeadsUpVisible;
private boolean mHeadsUpAnimatingAway;
private final FalsingManager mFalsingManager;
private final FalsingCollector mFalsingCollector;
@@ -600,6 +607,7 @@
private final PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
private final SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
private final ActiveNotificationsInteractor mActiveNotificationsInteractor;
+ private final HeadsUpNotificationInteractor mHeadsUpNotificationInteractor;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final KeyguardInteractor mKeyguardInteractor;
private final PowerInteractor mPowerInteractor;
@@ -765,6 +773,7 @@
ActivityStarter activityStarter,
SharedNotificationContainerInteractor sharedNotificationContainerInteractor,
ActiveNotificationsInteractor activeNotificationsInteractor,
+ HeadsUpNotificationInteractor headsUpNotificationInteractor,
ShadeAnimationInteractor shadeAnimationInteractor,
KeyguardViewConfigurator keyguardViewConfigurator,
DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
@@ -799,6 +808,7 @@
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mSharedNotificationContainerInteractor = sharedNotificationContainerInteractor;
mActiveNotificationsInteractor = activeNotificationsInteractor;
+ mHeadsUpNotificationInteractor = headsUpNotificationInteractor;
mKeyguardInteractor = keyguardInteractor;
mPowerInteractor = powerInteractor;
mKeyguardViewConfigurator = keyguardViewConfigurator;
@@ -1119,7 +1129,7 @@
controller.setup(mNotificationContainerParent));
// Dreaming->Lockscreen
- collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(DREAMING, LOCKSCREEN),
mDreamingToLockscreenTransition, mMainDispatcher);
collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(),
setDreamLockscreenTransitionAlpha(mNotificationStackScrollLayoutController),
@@ -1130,7 +1140,7 @@
// Gone -> Dreaming hosted in lockscreen
collectFlow(mView, mKeyguardTransitionInteractor
- .getGoneToDreamingLockscreenHostedTransition(),
+ .transition(GONE, DREAMING_LOCKSCREEN_HOSTED),
mGoneToDreamingLockscreenHostedTransition, mMainDispatcher);
collectFlow(mView, mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha(),
setTransitionAlpha(mNotificationStackScrollLayoutController),
@@ -1138,16 +1148,16 @@
// Lockscreen -> Dreaming hosted in lockscreen
collectFlow(mView, mKeyguardTransitionInteractor
- .getLockscreenToDreamingLockscreenHostedTransition(),
+ .transition(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED),
mLockscreenToDreamingLockscreenHostedTransition, mMainDispatcher);
// Dreaming hosted in lockscreen -> Lockscreen
collectFlow(mView, mKeyguardTransitionInteractor
- .getDreamingLockscreenHostedToLockscreenTransition(),
+ .transition(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN),
mDreamingLockscreenHostedToLockscreenTransition, mMainDispatcher);
// Occluded->Lockscreen
- collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(OCCLUDED, LOCKSCREEN),
mOccludedToLockscreenTransition, mMainDispatcher);
if (!MigrateClocksToBlueprint.isEnabled()) {
collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
@@ -1158,7 +1168,7 @@
}
// Lockscreen->Dreaming
- collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToDreamingTransition(),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING),
mLockscreenToDreamingTransition, mMainDispatcher);
if (!MigrateClocksToBlueprint.isEnabled()) {
collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(),
@@ -1170,7 +1180,7 @@
setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
// Gone->Dreaming
- collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(GONE, DREAMING),
mGoneToDreamingTransition, mMainDispatcher);
if (!MigrateClocksToBlueprint.isEnabled()) {
collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(),
@@ -1181,7 +1191,7 @@
setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
// Lockscreen->Occluded
- collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(LOCKSCREEN, OCCLUDED),
mLockscreenToOccludedTransition, mMainDispatcher);
if (!MigrateClocksToBlueprint.isEnabled()) {
collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(),
@@ -1201,6 +1211,21 @@
"mPrimaryBouncerToGoneTransitionViewModel.getNotificationAlpha()");
}, mMainDispatcher);
}
+
+ // Ensures that flags are updated when an activity launches
+ collectFlow(mView,
+ mShadeAnimationInteractor.isLaunchingActivity(),
+ isLaunchingActivity -> {
+ if (isLaunchingActivity) {
+ updateSystemUiStateFlags();
+ }
+ },
+ mMainDispatcher);
+
+ if (NotificationsHeadsUpRefactor.isEnabled()) {
+ collectFlow(mView, mHeadsUpNotificationInteractor.isHeadsUpOrAnimatingAway(),
+ setHeadsUpVisible(), mMainDispatcher);
+ }
}
@VisibleForTesting
@@ -3040,7 +3065,21 @@
mPanelAlphaEndAction = r;
}
+ private Consumer<Boolean> setHeadsUpVisible() {
+ return (Boolean isHeadsUpVisible) -> {
+ mHeadsUpVisible = isHeadsUpVisible;
+
+ if (isHeadsUpVisible) {
+ updateNotificationTranslucency();
+ }
+ updateExpansionAndVisibility();
+ updateGestureExclusionRect();
+ mKeyguardStatusBarViewController.updateForHeadsUp();
+ };
+ }
+
private void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
+ NotificationsHeadsUpRefactor.assertInLegacyMode();
mHeadsUpAnimatingAway = headsUpAnimatingAway;
mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway);
updateVisibility();
@@ -3056,13 +3095,16 @@
}
private boolean shouldPanelBeVisible() {
- boolean headsUpVisible = mHeadsUpAnimatingAway || mHeadsUpPinnedMode;
+ boolean headsUpVisible = NotificationsHeadsUpRefactor.isEnabled() ? mHeadsUpVisible
+ : (mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
return headsUpVisible || isExpanded() || mBouncerShowing;
}
private void setHeadsUpManager(HeadsUpManager headsUpManager) {
mHeadsUpManager = headsUpManager;
- mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
+ if (!NotificationsHeadsUpRefactor.isEnabled()) {
+ mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
+ }
mHeadsUpTouchHelper = new HeadsUpTouchHelper(
headsUpManager,
mStatusBarService,
@@ -3150,8 +3192,9 @@
}
private boolean isPanelVisibleBecauseOfHeadsUp() {
- return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
- && mBarState == StatusBarState.SHADE;
+ boolean headsUpVisible = NotificationsHeadsUpRefactor.isEnabled() ? mHeadsUpVisible
+ : (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway);
+ return headsUpVisible && mBarState == StatusBarState.SHADE;
}
private boolean isPanelVisibleBecauseScrimIsAnimatingOff() {
@@ -3464,6 +3507,7 @@
ipw.print("mExpandingFromHeadsUp="); ipw.println(mExpandingFromHeadsUp);
ipw.print("mCollapsedOnDown="); ipw.println(mCollapsedOnDown);
ipw.print("mClosingWithAlphaFadeOut="); ipw.println(mClosingWithAlphaFadeOut);
+ ipw.print("mHeadsUpVisible="); ipw.println(mHeadsUpVisible);
ipw.print("mHeadsUpAnimatingAway="); ipw.println(mHeadsUpAnimatingAway);
ipw.print("mShowIconsWhenExpanded="); ipw.println(mShowIconsWhenExpanded);
ipw.print("mIndicationBottomPadding="); ipw.println(mIndicationBottomPadding);
@@ -3632,7 +3676,8 @@
+ isFullyExpanded() + " inQs=" + mQsController.getExpanded());
}
mSysUiState
- .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE, isPanelExpanded())
+ .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE,
+ isPanelExpanded() && !isCollapsing())
.setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
isFullyExpanded() && !mQsController.getExpanded())
.setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
@@ -4368,6 +4413,8 @@
private final class ShadeHeadsUpChangedListener implements OnHeadsUpChangedListener {
@Override
public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
+ NotificationsHeadsUpRefactor.assertInLegacyMode();
+
if (inPinnedMode) {
mHeadsUpExistenceChangedRunnable.run();
updateNotificationTranslucency();
@@ -4384,9 +4431,7 @@
@Override
public void onHeadsUpPinned(NotificationEntry entry) {
- if (NotificationsHeadsUpRefactor.isEnabled()) {
- return;
- }
+ NotificationsHeadsUpRefactor.assertInLegacyMode();
if (!isKeyguardShowing()) {
mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, true);
@@ -4395,9 +4440,7 @@
@Override
public void onHeadsUpUnPinned(NotificationEntry entry) {
- if (NotificationsHeadsUpRefactor.isEnabled()) {
- return;
- }
+ NotificationsHeadsUpRefactor.assertInLegacyMode();
// When we're unpinning the notification via active edge they remain heads-upped,
// we need to make sure that an animation happens in this case, otherwise the
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index fb32b9f..adcb14a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -59,7 +59,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.ui.view.WindowRootViewComponent;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
@@ -117,7 +117,6 @@
private final AuthController mAuthController;
private final Lazy<SelectedUserInteractor> mUserInteractor;
private final Lazy<ShadeInteractor> mShadeInteractorLazy;
- private final SceneContainerFlags mSceneContainerFlags;
private final Lazy<CommunalInteractor> mCommunalInteractor;
private ViewGroup mWindowRootView;
private LayoutParams mLp;
@@ -166,7 +165,6 @@
ShadeWindowLogger logger,
Lazy<SelectedUserInteractor> userInteractor,
UserTracker userTracker,
- SceneContainerFlags sceneContainerFlags,
Lazy<CommunalInteractor> communalInteractor) {
mContext = context;
mWindowRootViewComponentFactory = windowRootViewComponentFactory;
@@ -186,7 +184,6 @@
dumpManager.registerCriticalDumpable("{slow}NotificationShadeWindowControllerImpl", this);
mAuthController = authController;
mUserInteractor = userInteractor;
- mSceneContainerFlags = sceneContainerFlags;
mCommunalInteractor = communalInteractor;
mLastKeyguardRotationAllowed = mKeyguardStateController.isKeyguardScreenRotationAllowed();
mLockScreenDisplayTimeout = context.getResources()
@@ -289,7 +286,7 @@
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mLp.privateFlags |= PRIVATE_FLAG_OPTIMIZE_MEASURE;
- if (mSceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
// This prevents the appearance and disappearance of the software keyboard (also known
// as the "IME") from scrolling/panning the window to make room for the keyboard.
//
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 6ac81d2..a763641 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -18,6 +18,8 @@
import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
import static com.android.systemui.flags.Flags.TRACKPAD_GESTURE_COMMON;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -221,7 +223,7 @@
mDisableSubpixelTextTransitionListener = new DisableSubpixelTextTransitionListener(mView);
bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container));
- collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
+ collectFlow(mView, keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING),
mLockscreenToDreamingTransition);
collectFlow(
mView,
@@ -382,12 +384,15 @@
float x = ev.getRawX();
float y = ev.getRawY();
if (mStatusBarViewController.touchIsWithinView(x, y)) {
- if (mStatusBarWindowStateController.windowIsShowing()) {
- mIsTrackingBarGesture = true;
- return logDownDispatch(ev, "sending touch to status bar",
- mStatusBarViewController.sendTouchToView(ev));
- } else {
- return logDownDispatch(ev, "hidden or hiding", true);
+ if (!(MigrateClocksToBlueprint.isEnabled()
+ && mPrimaryBouncerInteractor.isBouncerShowing())) {
+ if (mStatusBarWindowStateController.windowIsShowing()) {
+ mIsTrackingBarGesture = true;
+ return logDownDispatch(ev, "sending touch to status bar",
+ mStatusBarViewController.sendTouchToView(ev));
+ } else {
+ return logDownDispatch(ev, "hidden or hiding", true);
+ }
}
}
} else if (mIsTrackingBarGesture) {
@@ -654,6 +659,9 @@
mTouchCancelled = true;
}
mAmbientState.setSwipingUp(false);
+ if (MigrateClocksToBlueprint.isEnabled()) {
+ mDragDownHelper.stopDragging();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index ebebbe6..8c15817 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -124,7 +124,6 @@
// release focus immediately to kick off focus change transition
notificationShadeWindowController.setNotificationShadeFocusable(false)
notificationStackScrollLayout.cancelExpandHelper()
- sceneInteractor.changeScene(Scenes.Shade, "ShadeController.animateExpandShade")
if (delayed) {
scope.launch {
delay(125)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 648d4b5..a0c9391 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -19,7 +19,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.qs.QSContainerController
import com.android.systemui.qs.ui.adapter.QSSceneAdapterImpl
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.data.repository.PrivacyChipRepository
import com.android.systemui.shade.data.repository.PrivacyChipRepositoryImpl
import com.android.systemui.shade.data.repository.ShadeRepository
@@ -50,11 +50,10 @@
@Provides
@SysUISingleton
fun provideBaseShadeInteractor(
- sceneContainerFlags: SceneContainerFlags,
sceneContainerOn: Provider<ShadeInteractorSceneContainerImpl>,
sceneContainerOff: Provider<ShadeInteractorLegacyImpl>
): BaseShadeInteractor {
- return if (sceneContainerFlags.isEnabled()) {
+ return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
} else {
sceneContainerOff.get()
@@ -64,11 +63,10 @@
@Provides
@SysUISingleton
fun provideShadeController(
- sceneContainerFlags: SceneContainerFlags,
sceneContainerOn: Provider<ShadeControllerSceneImpl>,
sceneContainerOff: Provider<ShadeControllerImpl>
): ShadeController {
- return if (sceneContainerFlags.isEnabled()) {
+ return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
} else {
sceneContainerOff.get()
@@ -78,11 +76,10 @@
@Provides
@SysUISingleton
fun provideShadeAnimationInteractor(
- sceneContainerFlags: SceneContainerFlags,
sceneContainerOn: Provider<ShadeAnimationInteractorSceneContainerImpl>,
sceneContainerOff: Provider<ShadeAnimationInteractorLegacyImpl>
): ShadeAnimationInteractor {
- return if (sceneContainerFlags.isEnabled()) {
+ return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
} else {
sceneContainerOff.get()
@@ -92,11 +89,10 @@
@Provides
@SysUISingleton
fun provideShadeBackActionInteractor(
- sceneContainerFlags: SceneContainerFlags,
sceneContainerOn: Provider<ShadeBackActionInteractorImpl>,
sceneContainerOff: Provider<NotificationPanelViewController>
): ShadeBackActionInteractor {
- return if (sceneContainerFlags.isEnabled()) {
+ return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
} else {
sceneContainerOff.get()
@@ -106,11 +102,10 @@
@Provides
@SysUISingleton
fun provideShadeLockscreenInteractor(
- sceneContainerFlags: SceneContainerFlags,
sceneContainerOn: Provider<ShadeLockscreenInteractorImpl>,
sceneContainerOff: Provider<NotificationPanelViewController>
): ShadeLockscreenInteractor {
- return if (sceneContainerFlags.isEnabled()) {
+ return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
} else {
sceneContainerOff.get()
@@ -120,11 +115,10 @@
@Provides
@SysUISingleton
fun providePanelExpansionInteractor(
- sceneContainerFlags: SceneContainerFlags,
sceneContainerOn: Provider<PanelExpansionInteractorImpl>,
sceneContainerOff: Provider<NotificationPanelViewController>
): PanelExpansionInteractor {
- return if (sceneContainerFlags.isEnabled()) {
+ return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
} else {
sceneContainerOff.get()
@@ -134,11 +128,10 @@
@Provides
@SysUISingleton
fun provideQuickSettingsController(
- sceneContainerFlags: SceneContainerFlags,
sceneContainerOn: Provider<QuickSettingsControllerSceneImpl>,
sceneContainerOff: Provider<QuickSettingsControllerImpl>,
): QuickSettingsController {
- return if (sceneContainerFlags.isEnabled()) {
+ return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
} else {
sceneContainerOff.get()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index f5dd5e4..bc23778 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -33,7 +33,7 @@
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
@@ -78,15 +78,13 @@
@SysUISingleton
fun providesWindowRootView(
layoutInflater: LayoutInflater,
- sceneContainerFlags: SceneContainerFlags,
viewModelProvider: Provider<SceneContainerViewModel>,
containerConfigProvider: Provider<SceneContainerConfig>,
- flagsProvider: Provider<SceneContainerFlags>,
scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
layoutInsetController: NotificationInsetsController,
sceneDataSourceDelegator: Provider<SceneDataSourceDelegator>,
): WindowRootView {
- return if (sceneContainerFlags.isEnabled()) {
+ return if (SceneContainerFlag.isEnabled) {
checkNoSceneDuplicates(scenesProvider.get())
val sceneWindowRootView =
layoutInflater.inflate(R.layout.scene_window_root, null) as SceneWindowRootView
@@ -95,7 +93,6 @@
containerConfig = containerConfigProvider.get(),
sharedNotificationContainer =
sceneWindowRootView.requireViewById(R.id.shared_notification_container),
- flags = flagsProvider.get(),
scenes = scenesProvider.get(),
layoutInsetController = layoutInsetController,
sceneDataSourceDelegator = sceneDataSourceDelegator.get(),
@@ -115,9 +112,8 @@
@SysUISingleton
fun providesNotificationShadeWindowView(
root: WindowRootView,
- sceneContainerFlags: SceneContainerFlags,
): NotificationShadeWindowView {
- if (sceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
return root.requireViewById(R.id.legacy_window_root)
}
return root as NotificationShadeWindowView?
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
index d68e28c..0b45c08 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -82,7 +82,7 @@
override val isShadeTouchable: Flow<Boolean> =
combine(
powerInteractor.isAsleep,
- keyguardTransitionInteractor.isInTransitionToStateWhere { it == KeyguardState.AOD },
+ keyguardTransitionInteractor.isInTransitionToState(KeyguardState.AOD),
keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING },
) { isAsleep, goingToSleep, isPulsing ->
when {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 72a9c8d..6c76061 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -22,9 +22,11 @@
import android.icu.text.DateFormat
import android.icu.text.DisplayContext
import android.os.UserHandle
+import android.provider.Settings
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.res.R
@@ -54,6 +56,7 @@
constructor(
@Application private val applicationScope: CoroutineScope,
context: Context,
+ private val activityStarter: ActivityStarter,
shadeInteractor: ShadeInteractor,
mobileIconsInteractor: MobileIconsInteractor,
val mobileIconsViewModel: MobileIconsViewModel,
@@ -136,6 +139,14 @@
clockInteractor.launchClockActivity()
}
+ /** Notifies that the shadeCarrierGroup was clicked. */
+ fun onShadeCarrierGroupClicked() {
+ activityStarter.postStartActivityDismissingKeyguard(
+ Intent(Settings.ACTION_WIRELESS_SETTINGS),
+ 0
+ )
+ }
+
private fun updateDateTexts(invalidateFormats: Boolean) {
if (invalidateFormats) {
longerDateFormat.value = getFormatFromPattern(longerPattern)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 980f665a..5b76acb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -37,6 +37,7 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -64,6 +65,7 @@
private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
private val footerActionsController: FooterActionsController,
private val sceneInteractor: SceneInteractor,
+ private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
) {
val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
combine(
@@ -106,6 +108,14 @@
val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode
+ /**
+ * Amount of X-axis translation to apply to various elements as the unfolded foldable is folded
+ * slightly, in pixels.
+ */
+ fun unfoldTranslationX(isOnStartSide: Boolean): Flow<Float> {
+ return unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide)
+ }
+
/** Notifies that some content in the shade was clicked. */
fun onContentClicked() {
if (!isClickable.value) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index e7b159a..d955349 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -50,6 +50,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Pair;
import android.util.SparseArray;
import android.view.KeyEvent;
@@ -516,7 +517,7 @@
/**
* @see IStatusBar#showMediaOutputSwitcher
*/
- default void showMediaOutputSwitcher(String packageName) {}
+ default void showMediaOutputSwitcher(String packageName, UserHandle userHandle) {}
/**
* @see IStatusBar#confirmImmersivePrompt
@@ -1361,7 +1362,7 @@
}
}
@Override
- public void showMediaOutputSwitcher(String packageName) {
+ public void showMediaOutputSwitcher(String packageName, UserHandle userHandle) {
int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
throw new SecurityException("Call only allowed from system server.");
@@ -1369,6 +1370,7 @@
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = packageName;
+ args.arg2 = userHandle;
mHandler.obtainMessage(MSG_SHOW_MEDIA_OUTPUT_SWITCHER, args).sendToTarget();
}
}
@@ -1939,8 +1941,10 @@
case MSG_SHOW_MEDIA_OUTPUT_SWITCHER:
args = (SomeArgs) msg.obj;
String clientPackageName = (String) args.arg1;
+ UserHandle clientUserHandle = (UserHandle) args.arg2;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showMediaOutputSwitcher(clientPackageName);
+ mCallbacks.get(i).showMediaOutputSwitcher(clientPackageName,
+ clientUserHandle);
}
break;
case MSG_CONFIRM_IMMERSIVE_PROMPT:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 815236e..09985f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -195,20 +195,20 @@
private boolean mOrganizationOwnedDevice;
// these all assume the device is plugged in (wired/wireless/docked) AND chargingOrFull:
- private boolean mPowerPluggedIn;
- private boolean mPowerPluggedInWired;
- private boolean mPowerPluggedInWireless;
- private boolean mPowerPluggedInDock;
+ protected boolean mPowerPluggedIn;
+ protected boolean mPowerPluggedInWired;
+ protected boolean mPowerPluggedInWireless;
+ protected boolean mPowerPluggedInDock;
private boolean mPowerCharged;
private boolean mBatteryDefender;
private boolean mEnableBatteryDefender;
private boolean mIncompatibleCharger;
- private int mChargingSpeed;
+ protected int mChargingSpeed;
private int mChargingWattage;
private int mBatteryLevel;
private boolean mBatteryPresent = true;
- private long mChargingTimeRemaining;
+ protected long mChargingTimeRemaining;
private Pair<String, BiometricSourceType> mBiometricErrorMessageToShowOnScreenOn;
private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
private final FaceHelpMessageDeferral mFaceAcquiredMessageDeferral;
@@ -1053,20 +1053,24 @@
* Assumption: device is charging
*/
protected String computePowerIndication() {
- int chargingId;
if (mBatteryDefender) {
- chargingId = R.string.keyguard_plugged_in_charging_limited;
String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f);
- return mContext.getResources().getString(chargingId, percentage);
+ return mContext.getResources().getString(
+ R.string.keyguard_plugged_in_charging_limited, percentage);
} else if (mPowerPluggedIn && mIncompatibleCharger) {
- chargingId = R.string.keyguard_plugged_in_incompatible_charger;
String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f);
- return mContext.getResources().getString(chargingId, percentage);
+ return mContext.getResources().getString(
+ R.string.keyguard_plugged_in_incompatible_charger, percentage);
} else if (mPowerCharged) {
return mContext.getResources().getString(R.string.keyguard_charged);
}
+ return computePowerChargingStringIndication();
+ }
+
+ protected String computePowerChargingStringIndication() {
final boolean hasChargingTime = mChargingTimeRemaining > 0;
+ int chargingId;
if (mPowerPluggedInWired) {
switch (mChargingSpeed) {
case BatteryStatus.CHARGING_FAST:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 519d719..e3db626 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -959,7 +959,7 @@
anim.start()
}
- private fun stopDragging() {
+ fun stopDragging() {
if (startingChild != null) {
cancelChildExpansion(startingChild!!)
startingChild = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index bb6ee24..f8193a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -57,6 +57,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
+import com.android.systemui.Flags;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.NotificationContentDescription;
import com.android.systemui.statusbar.notification.NotificationDozeHelper;
@@ -208,6 +209,10 @@
initializeDecorColor();
reloadDimens();
maybeUpdateIconScaleDimens();
+
+ if (Flags.statusBarMonochromeIconsFix()) {
+ setCropToPadding(true);
+ }
}
/** Should always be preceded by {@link #reloadDimens()} */
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 e5b6497..594c191 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -35,7 +35,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
import com.android.systemui.power.domain.interactor.PowerInteractor;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeSurface;
@@ -61,8 +61,6 @@
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import javax.inject.Provider;
-
import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
@@ -70,6 +68,8 @@
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
+import javax.inject.Provider;
+
/**
* This module provides instances needed to construct {@link CentralSurfacesImpl}. These are moved to
* this separate from {@link CentralSurfacesModule} module so that components that wish to build
@@ -185,10 +185,9 @@
@Provides
@SysUISingleton
static ShadeSurface provideShadeSurface(
- SceneContainerFlags sceneContainerFlags,
Provider<ShadeSurfaceImpl> sceneContainerOn,
Provider<NotificationPanelViewController> sceneContainerOff) {
- if (sceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
return sceneContainerOn.get();
} else {
return sceneContainerOff.get();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 9ce38db..8b673c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -49,7 +49,6 @@
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
import android.view.ContentInfo;
import androidx.annotation.NonNull;
@@ -150,10 +149,8 @@
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
private InflationTask mRunningTask = null;
- private Throwable mDebugThrowable;
public CharSequence remoteInputTextWhenReset;
public long lastRemoteInputSent = NOT_LAUNCHED_YET;
- public final ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
private final MutableStateFlow<CharSequence> mHeadsUpStatusBarText =
StateFlowKt.MutableStateFlow(null);
@@ -190,11 +187,6 @@
private boolean mBlockable;
/**
- * The {@link SystemClock#elapsedRealtime()} when this notification entry was created.
- */
- public long mCreationElapsedRealTime;
-
- /**
* Whether this notification has ever been a non-sticky HUN.
*/
private boolean mIsDemoted = false;
@@ -264,13 +256,8 @@
mKey = sbn.getKey();
setSbn(sbn);
setRanking(ranking);
- mCreationElapsedRealTime = SystemClock.elapsedRealtime();
}
- @VisibleForTesting
- public void setCreationElapsedRealTime(long time) {
- mCreationElapsedRealTime = time;
- }
@Override
public NotificationEntry getRepresentativeEntry() {
return this;
@@ -581,19 +568,6 @@
return mRunningTask;
}
- /**
- * Set a throwable that is used for debugging
- *
- * @param debugThrowable the throwable to save
- */
- public void setDebugThrowable(Throwable debugThrowable) {
- mDebugThrowable = debugThrowable;
- }
-
- public Throwable getDebugThrowable() {
- return mDebugThrowable;
- }
-
public void onRemoteInputInserted() {
lastRemoteInputSent = NOT_LAUNCHED_YET;
remoteInputTextWhenReset = null;
@@ -749,12 +723,6 @@
return row != null && row.areChildrenExpanded();
}
-
- //TODO: probably less confusing to say "is group fully visible"
- public boolean isGroupNotFullyVisible() {
- return row == null || row.isGroupNotFullyVisible();
- }
-
public NotificationGuts getGuts() {
if (row != null) return row.getGuts();
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRepository.kt
index ed8c056..e9306a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.data.repository
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
/**
* A repository of currently displayed heads up notifications.
@@ -29,13 +30,15 @@
/**
* True if we are exiting the headsUp pinned mode, and some notifications might still be
- * animating out. This is used to keep the touchable regions in a reasonable state.
+ * animating out. This is used to keep their view container visible.
*/
- val headsUpAnimatingAway: Flow<Boolean>
+ val isHeadsUpAnimatingAway: StateFlow<Boolean>
/** The heads up row that should be displayed on top. */
val topHeadsUpRow: Flow<HeadsUpRowRepository?>
/** Set of currently active top-level heads up rows to be displayed. */
val activeHeadsUpRows: Flow<Set<HeadsUpRowRepository>>
+
+ fun setHeadsUpAnimatingAway(animatingAway: Boolean)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
index d1dd7b5..98b52ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
@@ -29,7 +29,7 @@
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-class HeadsUpNotificationInteractor @Inject constructor(repository: HeadsUpRepository) {
+class HeadsUpNotificationInteractor @Inject constructor(private val repository: HeadsUpRepository) {
val topHeadsUpRow: Flow<HeadsUpRowKey?> = repository.topHeadsUpRow
@@ -60,13 +60,16 @@
}
val isHeadsUpOrAnimatingAway: Flow<Boolean> =
- combine(hasPinnedRows, repository.headsUpAnimatingAway) { hasPinnedRows, animatingAway ->
+ combine(hasPinnedRows, repository.isHeadsUpAnimatingAway) { hasPinnedRows, animatingAway ->
hasPinnedRows || animatingAway
}
fun headsUpRow(key: HeadsUpRowKey): HeadsUpRowInteractor =
HeadsUpRowInteractor(key as HeadsUpRowRepository)
fun elementKeyFor(key: HeadsUpRowKey) = (key as HeadsUpRowRepository).elementKey
+ fun setHeadsUpAnimatingAway(animatingAway: Boolean) {
+ repository.setHeadsUpAnimatingAway(animatingAway)
+ }
}
class HeadsUpRowInteractor(repository: HeadsUpRowRepository)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index c4d9ab7..9619aca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -27,6 +27,7 @@
import android.hardware.display.AmbientDisplayConfiguration
import android.os.Handler
import android.os.PowerManager
+import android.provider.Settings
import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED
import android.provider.Settings.Global.HEADS_UP_OFF
import com.android.systemui.dagger.qualifiers.Main
@@ -42,6 +43,7 @@
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SystemSettings
import com.android.systemui.util.time.SystemClock
class PeekDisabledSuppressor(
@@ -231,6 +233,7 @@
class AvalancheSuppressor(
private val avalancheProvider: AvalancheProvider,
private val systemClock: SystemClock,
+ private val systemSettings: SystemSettings,
) :
VisualInterruptionFilter(
types = setOf(PEEK, PULSE),
@@ -253,12 +256,23 @@
}
override fun shouldSuppress(entry: NotificationEntry): Boolean {
- val timeSinceAvalanche = systemClock.currentTimeMillis() - avalancheProvider.startTime
- val isActive = timeSinceAvalanche < avalancheProvider.timeoutMs
+ if (!isCooldownEnabled()) {
+ reason = "FALSE avalanche cooldown setting DISABLED"
+ return false
+ }
+ val timeSinceAvalancheMs = systemClock.currentTimeMillis() - avalancheProvider.startTime
+ val timedOut = timeSinceAvalancheMs >= avalancheProvider.timeoutMs
+ if (timedOut) {
+ reason = "FALSE avalanche event TIMED OUT. " +
+ "${timeSinceAvalancheMs/1000} seconds since last avalanche"
+ return false
+ }
val state = calculateState(entry)
- val suppress = isActive && state == State.SUPPRESS
- reason = "avalanche suppress=$suppress isActive=$isActive state=$state"
- return suppress
+ if (state != State.SUPPRESS) {
+ reason = "FALSE avalanche IN ALLOWLIST: $state"
+ return false
+ }
+ return true
}
private fun calculateState(entry: NotificationEntry): State {
@@ -294,4 +308,11 @@
}
return State.SUPPRESS
}
+
+ private fun isCooldownEnabled(): Boolean {
+ return systemSettings.getInt(
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+ /* def */ 1
+ ) == 1
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 375b6e5c..e6d97c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -40,6 +40,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.EventLog
import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SystemSettings
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -61,7 +62,8 @@
private val systemClock: SystemClock,
private val uiEventLogger: UiEventLogger,
private val userTracker: UserTracker,
- private val avalancheProvider: AvalancheProvider
+ private val avalancheProvider: AvalancheProvider,
+ private val systemSettings: SystemSettings
) : VisualInterruptionDecisionProvider {
init {
@@ -170,7 +172,7 @@
addFilter(AlertKeyguardVisibilitySuppressor(keyguardNotificationVisibilityProvider))
if (NotificationAvalancheSuppression.isEnabled) {
- addFilter(AvalancheSuppressor(avalancheProvider, systemClock))
+ addFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings))
avalancheProvider.register()
}
started = true
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 9e0b16c..0c8518f 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
@@ -1767,7 +1767,8 @@
*/
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
this(context, attrs, context);
- Log.e(TAG, "This constructor shouldn't be called");
+ // NOTE(b/317503801): Always crash when using the insecure constructor.
+ throw new UnsupportedOperationException("Insecure constructor");
}
/**
@@ -2804,12 +2805,7 @@
}
public boolean isExpanded(boolean allowOnKeyguard) {
- if (DEBUG) {
- if (!mShowingPublicInitialized && !allowOnKeyguard) {
- Log.d(TAG, "mShowingPublic is not initialized.");
- }
- }
- return !mShowingPublic && (!mOnKeyguard || allowOnKeyguard)
+ return (!shouldShowPublic()) && (!mOnKeyguard || allowOnKeyguard)
&& (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
|| isUserExpanded());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index 5fbcebd..35afda7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -33,6 +33,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.util.time.SystemClock;
+import java.util.concurrent.Executor;
+
import javax.inject.Inject;
/**
@@ -58,10 +60,22 @@
}
/**
- * Inflates a new notificationView. This should not be called twice on this object
+ * Inflates a new notificationView asynchronously, calling the {@code listener} on the main
+ * thread when done. This should not be called twice on this object.
*/
public void inflate(Context context, ViewGroup parent, NotificationEntry entry,
RowInflationFinishedListener listener) {
+ inflate(context, parent, entry, null, listener);
+ }
+
+ /**
+ * Inflates a new notificationView asynchronously, calling the {@code listener} on the supplied
+ * {@code listenerExecutor} (or the main thread if null) when done. This should not be called
+ * twice on this object.
+ */
+ @VisibleForTesting
+ public void inflate(Context context, ViewGroup parent, NotificationEntry entry,
+ @Nullable Executor listenerExecutor, RowInflationFinishedListener listener) {
if (TRACE_ORIGIN) {
mInflateOrigin = new Throwable("inflate requested here");
}
@@ -72,7 +86,7 @@
mLogger.logInflateStart(entry);
mInflateStartTimeMs = mSystemClock.elapsedRealtime();
- inflater.inflate(R.layout.status_bar_notification_row, parent, this);
+ inflater.inflate(R.layout.status_bar_notification_row, parent, listenerExecutor, this);
}
private RowAsyncLayoutInflater makeRowInflater(NotificationEntry entry) {
@@ -80,12 +94,12 @@
}
@VisibleForTesting
- static class RowAsyncLayoutInflater implements AsyncLayoutFactory {
+ public static class RowAsyncLayoutInflater implements AsyncLayoutFactory {
private final NotificationEntry mEntry;
private final SystemClock mSystemClock;
private final RowInflaterTaskLogger mLogger;
- RowAsyncLayoutInflater(NotificationEntry entry, SystemClock systemClock,
+ public RowAsyncLayoutInflater(NotificationEntry entry, SystemClock systemClock,
RowInflaterTaskLogger logger) {
mEntry = entry;
mSystemClock = systemClock;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
index d6118a0..d3c874c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row
+import android.app.Flags
import android.app.Notification
import android.app.Notification.MessagingStyle
import android.app.Person
@@ -131,7 +132,7 @@
val senderName =
systemUiContext.resources.getString(
R.string.conversation_single_line_name_display,
- name
+ if (Flags.cleanUpSpansAndNewLines()) name?.toString() else name
)
// We need to find back-up values for those texts if they are needed and empty
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 82559de..8a1a4f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -111,6 +111,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
+import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds;
@@ -450,7 +451,9 @@
private boolean mIsClipped;
private Rect mRequestedClipBounds;
private boolean mInHeadsUpPinnedMode;
- private boolean mHeadsUpAnimatingAway;
+ @VisibleForTesting
+ boolean mHeadsUpAnimatingAway;
+ private Consumer<Boolean> mHeadsUpAnimatingAwayListener;
private int mStatusBarState;
private int mUpcomingStatusBarState;
private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
@@ -4084,7 +4087,14 @@
mSwipeHelper.setIsExpanded(isExpanded);
if (changed) {
mWillExpand = false;
- if (!mIsExpanded) {
+ if (mIsExpanded) {
+ // Resetting headsUpAnimatingAway on Shade expansion avoids delays caused by
+ // waiting for all child animations to finish.
+ // TODO(b/328390331) Do we need to reset this on QS expanded as well?
+ if (NotificationsHeadsUpRefactor.isEnabled()) {
+ setHeadsUpAnimatingAway(false);
+ }
+ } else {
mGroupExpansionManager.collapseGroups();
mExpandHelper.cancelImmediately();
if (!mIsExpansionChanging) {
@@ -4190,6 +4200,9 @@
void onChildAnimationFinished() {
setAnimationRunning(false);
+ if (NotificationsHeadsUpRefactor.isEnabled()) {
+ setHeadsUpAnimatingAway(false);
+ }
requestChildrenUpdate();
runAnimationFinishedRunnables();
clearTransient();
@@ -4509,18 +4522,18 @@
mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
if (areNotificationsHiddenInShade) {
- updateEmptyShadeView(R.string.dnd_suppressing_shade_text, 0, 0);
+ updateEmptyShadeViewResources(R.string.dnd_suppressing_shade_text, 0, 0);
} else if (hasFilteredOutSeenNotifications) {
- updateEmptyShadeView(
+ updateEmptyShadeViewResources(
R.string.no_unseen_notif_text,
R.string.unlock_to_see_notif_text,
R.drawable.ic_friction_lock_closed);
} else {
- updateEmptyShadeView(R.string.empty_shade_text, 0, 0);
+ updateEmptyShadeViewResources(R.string.empty_shade_text, 0, 0);
}
}
- private void updateEmptyShadeView(
+ private void updateEmptyShadeViewResources(
@StringRes int newTextRes,
@StringRes int newFooterTextRes,
@DrawableRes int newFooterIconRes) {
@@ -4717,6 +4730,7 @@
}
public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
+ NotificationsHeadsUpRefactor.assertInLegacyMode();
ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
generateHeadsUpAnimation(row, isHeadsUp);
}
@@ -4750,6 +4764,9 @@
mNeedsAnimation = true;
if (!mIsExpanded && !mWillExpand && !isHeadsUp) {
row.setHeadsUpAnimatingAway(true);
+ if (NotificationsHeadsUpRefactor.isEnabled()) {
+ setHeadsUpAnimatingAway(true);
+ }
}
requestChildrenUpdate();
}
@@ -4939,11 +4956,28 @@
updateClipping();
}
+ /** TODO(b/328390331) make this private, when {@link NotificationsHeadsUpRefactor} is removed */
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
- mHeadsUpAnimatingAway = headsUpAnimatingAway;
+ if (mHeadsUpAnimatingAway != headsUpAnimatingAway) {
+ mHeadsUpAnimatingAway = headsUpAnimatingAway;
+ if (mHeadsUpAnimatingAwayListener != null) {
+ mHeadsUpAnimatingAwayListener.accept(headsUpAnimatingAway);
+ }
+ }
updateClipping();
}
+ /**
+ * Sets a listener to be notified about the heads up disappear animation state changes. If there
+ * are overlapping animations, it will receive updates when the first disappar animation has
+ * started, and when the last has finished.
+ *
+ * @param headsUpAnimatingAwayListener to be notified about disappear animation state changes.
+ */
+ public void setHeadsUpAnimatingAwayListener(
+ Consumer<Boolean> headsUpAnimatingAwayListener) {
+ mHeadsUpAnimatingAwayListener = headsUpAnimatingAwayListener;
+ }
@VisibleForTesting
public void setStatusBarState(int statusBarState) {
mStatusBarState = statusBarState;
@@ -5338,7 +5372,8 @@
mActivityStarter.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
});
setEmptyShadeView(view);
- updateEmptyShadeView(
+ view.setVisible(oldView != null && oldView.isVisible(), /* animate = */ false);
+ updateEmptyShadeViewResources(
oldView == null ? R.string.empty_shade_text : oldView.getTextResource(),
oldView == null ? 0 : oldView.getFooterTextResource(),
oldView == null ? 0 : oldView.getFooterIconResource());
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 06479e5..ea72c9b 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
@@ -1482,6 +1482,7 @@
}
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
+ NotificationsHeadsUpRefactor.assertInLegacyMode();
mView.setHeadsUpAnimatingAway(headsUpAnimatingAway);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 5eaccd9..e980794 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -594,7 +594,11 @@
);
if (view instanceof FooterView) {
if (FooterViewRefactor.isEnabled()) {
- if (((FooterView) view).shouldBeHidden()) {
+ // TODO(b/333445519): shouldBeHidden should reflect whether the shade is closed
+ // already, so we shouldn't need to use ambientState here. However, currently it
+ // doesn't get updated quickly enough and can cause the footer to flash when
+ // closing the shade. As such, we temporarily also check the ambientState directly.
+ if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) {
viewState.hidden = true;
} else {
final float footerEnd = algorithmState.mCurrentExpandedYPosition
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 741102b..cf5366b 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
@@ -29,7 +29,6 @@
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
@@ -49,7 +48,6 @@
class SharedNotificationContainerBinder
@Inject
constructor(
- private val sceneContainerFlags: SceneContainerFlags,
private val controller: NotificationStackScrollLayoutController,
private val notificationStackSizeCalculator: NotificationStackSizeCalculator,
private val notificationScrollViewBinder: NotificationScrollViewBinder,
@@ -130,7 +128,7 @@
.collect { controller.setMaxDisplayedNotifications(it) }
}
- if (!sceneContainerFlags.isEnabled()) {
+ if (!SceneContainerFlag.isEnabled) {
launch {
viewModel.bounds.collect {
val animate =
@@ -166,7 +164,7 @@
}
}
- if (sceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
disposables += notificationScrollViewBinder.bindWhileAttached()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index 5ab5857..3a89630 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
@@ -31,6 +32,7 @@
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackInteractor
import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
+import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.ui.AnimatableEvent
import com.android.systemui.util.ui.AnimatedValue
@@ -64,7 +66,8 @@
userSetupInteractor: UserSetupInteractor,
zenModeInteractor: ZenModeInteractor,
@Background bgDispatcher: CoroutineDispatcher,
-) {
+ dumpManager: DumpManager,
+) : FlowDumperImpl(dumpManager) {
/**
* We want the NSSL to be unimportant for accessibility when there are no notifications in it
* while the device is on lock screen, to avoid an unlabelled NSSL view in TalkBack. Otherwise,
@@ -81,8 +84,9 @@
) { hasNotifications, isShowingOnLockscreen ->
hasNotifications || !isShowingOnLockscreen
}
- .flowOn(bgDispatcher)
.distinctUntilChanged()
+ .dumpWhileCollecting("isImportantForAccessibility")
+ .flowOn(bgDispatcher)
}
}
@@ -105,8 +109,9 @@
else -> true
}
}
- .flowOn(bgDispatcher)
.distinctUntilChanged()
+ .dumpWhileCollecting("shouldShowEmptyShadeView")
+ .flowOn(bgDispatcher)
}
}
@@ -125,8 +130,9 @@
// the footer to be counted as part of the shade for measurements.
shadeInteractor.shadeExpansion
.map { it == 0f }
- .flowOn(bgDispatcher)
.distinctUntilChanged()
+ .dumpWhileCollecting("shouldHideFooterView")
+ .flowOn(bgDispatcher)
}
}
@@ -173,7 +179,6 @@
else -> VisibilityChange.APPEAR_WITH_ANIMATION
}
}
- .flowOn(bgDispatcher)
.distinctUntilChanged(
// Equivalent unless visibility changes
areEquivalent = { a: VisibilityChange, b: VisibilityChange ->
@@ -199,6 +204,8 @@
AnimatableEvent(visibilityChange.visible, shouldAnimate)
}
.toAnimatedValueFlow()
+ .dumpWhileCollecting("shouldIncludeFooterView")
+ .flowOn(bgDispatcher)
}
}
@@ -213,7 +220,9 @@
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(false)
} else {
- zenModeInteractor.areNotificationsHiddenInShade
+ zenModeInteractor.areNotificationsHiddenInShade.dumpWhileCollecting(
+ "areNotificationsHiddenInShade"
+ )
}
}
@@ -222,7 +231,9 @@
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(false)
} else {
- seenNotificationsInteractor.hasFilteredOutSeenNotifications
+ seenNotificationsInteractor.hasFilteredOutSeenNotifications.dumpWhileCollecting(
+ "hasFilteredOutSeenNotifications"
+ )
}
}
@@ -230,7 +241,9 @@
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(false)
} else {
- activeNotificationsInteractor.hasClearableAlertingNotifications
+ activeNotificationsInteractor.hasClearableAlertingNotifications.dumpWhileCollecting(
+ "hasClearableAlertingNotifications"
+ )
}
}
@@ -238,7 +251,9 @@
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(false)
} else {
- activeNotificationsInteractor.hasNonClearableSilentNotifications
+ activeNotificationsInteractor.hasNonClearableSilentNotifications.dumpWhileCollecting(
+ "hasNonClearableSilentNotifications"
+ )
}
}
@@ -246,7 +261,7 @@
if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(null)
} else {
- headsUpNotificationInteractor.topHeadsUpRow
+ headsUpNotificationInteractor.topHeadsUpRow.dumpWhileCollecting("topHeadsUpRow")
}
}
@@ -254,15 +269,20 @@
if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(emptySet())
} else {
- headsUpNotificationInteractor.pinnedHeadsUpRows
+ headsUpNotificationInteractor.pinnedHeadsUpRows.dumpWhileCollecting("pinnedHeadsUpRows")
}
}
val headsUpAnimationsEnabled: Flow<Boolean> by lazy {
- combine(keyguardInteractor.isKeyguardShowing, shadeInteractor.isShadeFullyExpanded) {
- (isKeyguardShowing, isShadeFullyExpanded) ->
- // TODO(b/325936094) use isShadeFullyCollapsed instead
- !isKeyguardShowing && !isShadeFullyExpanded
+ if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+ flowOf(false)
+ } else {
+ combine(keyguardInteractor.isKeyguardShowing, shadeInteractor.isShadeFullyExpanded) {
+ (isKeyguardShowing, isShadeFullyExpanded) ->
+ // TODO(b/325936094) use isShadeFullyCollapsed instead
+ !isKeyguardShowing && !isShadeFullyExpanded
+ }
+ .dumpWhileCollecting("headsUpAnimationsEnabled")
}
}
@@ -270,7 +290,7 @@
if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(false)
} else {
- headsUpNotificationInteractor.hasPinnedRows
+ headsUpNotificationInteractor.hasPinnedRows.dumpWhileCollecting("hasPinnedHeadsUpRow")
}
}
@@ -279,4 +299,8 @@
HeadsUpRowViewModel(headsUpNotificationInteractor.headsUpRow(key))
fun elementKeyFor(key: HeadsUpRowKey): Any = headsUpNotificationInteractor.elementKeyFor(key)
+
+ fun setHeadsUpAnimatingAway(animatingAway: Boolean) {
+ headsUpNotificationInteractor.setHeadsUpAnimatingAway(animatingAway)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index b284179..ca19da5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -22,7 +22,7 @@
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
@@ -43,7 +43,6 @@
dumpManager: DumpManager,
private val interactor: NotificationStackAppearanceInteractor,
shadeInteractor: ShadeInteractor,
- flags: SceneContainerFlags,
featureFlags: FeatureFlagsClassic,
private val keyguardInteractor: KeyguardInteractor,
) : FlowDumperImpl(dumpManager) {
@@ -51,7 +50,7 @@
val isVisualDebuggingEnabled: Boolean = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES)
/** DEBUG: whether the debug logging should be output. */
- val isDebugLoggingEnabled: Boolean = flags.isEnabled()
+ val isDebugLoggingEnabled: Boolean = SceneContainerFlag.isEnabled
/** Notifies that the bounds of the notification scrim have changed. */
fun onScrimBoundsChanged(bounds: ShadeScrimBounds?) {
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 0486ef5..37bbbd0 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
@@ -57,6 +57,7 @@
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludedToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
@@ -65,6 +66,7 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.util.kotlin.BooleanFlowOperators.and
import com.android.systemui.util.kotlin.BooleanFlowOperators.or
import com.android.systemui.util.kotlin.FlowDumperImpl
@@ -79,6 +81,7 @@
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
@@ -121,11 +124,13 @@
LockscreenToPrimaryBouncerTransitionViewModel,
private val lockscreenToOccludedTransitionViewModel: LockscreenToOccludedTransitionViewModel,
private val occludedToAodTransitionViewModel: OccludedToAodTransitionViewModel,
+ private val occludedToGoneTransitionViewModel: OccludedToGoneTransitionViewModel,
private val occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
private val primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
private val primaryBouncerToLockscreenTransitionViewModel:
PrimaryBouncerToLockscreenTransitionViewModel,
private val aodBurnInViewModel: AodBurnInViewModel,
+ unfoldTransitionInteractor: UnfoldTransitionInteractor,
) : FlowDumperImpl(dumpManager) {
private val statesForConstrainedNotifications: Set<KeyguardState> =
setOf(AOD, LOCKSCREEN, DOZING, ALTERNATE_BOUNCER, PRIMARY_BOUNCER)
@@ -207,9 +212,7 @@
keyguardTransitionInteractor.finishedKeyguardState.map {
statesForConstrainedNotifications.contains(it)
},
- keyguardTransitionInteractor
- .isInTransitionWhere { from, to -> from == LOCKSCREEN || to == LOCKSCREEN }
- .onStart { emit(false) }
+ keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
) { constrainedNotificationState, transitioningToOrFromLockscreen ->
constrainedNotificationState || transitioningToOrFromLockscreen
}
@@ -241,11 +244,10 @@
keyguardTransitionInteractor.finishedKeyguardState.map { state ->
state == GLANCEABLE_HUB
},
- keyguardTransitionInteractor
- .isInTransitionWhere { from, to ->
- from == GLANCEABLE_HUB || to == GLANCEABLE_HUB
- }
- .onStart { emit(false) }
+ or(
+ keyguardTransitionInteractor.isInTransitionToState(GLANCEABLE_HUB),
+ keyguardTransitionInteractor.isInTransitionFromState(GLANCEABLE_HUB),
+ ),
) { isOnGlanceableHub, transitioningToOrFromHub ->
isOnGlanceableHub || transitioningToOrFromHub
}
@@ -290,12 +292,10 @@
var aodTransitionIsComplete = true
return combine(
isOnLockscreenWithoutShade,
- keyguardTransitionInteractor
- .isInTransitionWhere(
- fromStatePredicate = { it == LOCKSCREEN },
- toStatePredicate = { it == AOD }
- )
- .onStart { emit(false) },
+ keyguardTransitionInteractor.isInTransition(
+ from = LOCKSCREEN,
+ to = AOD,
+ ),
::Pair
)
.transformWhile { (isOnLockscreenWithoutShade, aodTransitionIsRunning) ->
@@ -350,7 +350,8 @@
*
* When the shade is expanding, the position is controlled by... the shade.
*/
- val bounds: StateFlow<NotificationContainerBounds> =
+ val bounds: StateFlow<NotificationContainerBounds> by lazy {
+ SceneContainerFlag.assertInLegacyMode()
combine(
isOnLockscreenWithoutShade,
keyguardInteractor.notificationContainerBounds,
@@ -380,6 +381,7 @@
initialValue = NotificationContainerBounds(),
)
.dumpValue("bounds")
+ }
/**
* Ensure view is visible when the shade/qs are expanded. Also, as QS is expanding, fade out
@@ -475,6 +477,7 @@
lockscreenToOccludedTransitionViewModel.lockscreenAlpha,
lockscreenToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
occludedToAodTransitionViewModel.lockscreenAlpha,
+ occludedToGoneTransitionViewModel.notificationAlpha(viewState),
occludedToLockscreenTransitionViewModel.lockscreenAlpha,
primaryBouncerToGoneTransitionViewModel.notificationAlpha,
primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
@@ -577,14 +580,20 @@
.dumpWhileCollecting("translationY")
}
- /**
- * The container may need to be translated in the x direction as the keyguard fades out, such as
- * when swiping open the glanceable hub from the lockscreen.
- */
+ /** Horizontal translation to apply to the container. */
val translationX: Flow<Float> =
merge(
+ // The container may need to be translated along the X axis as the keyguard fades
+ // out, such as when swiping open the glanceable hub from the lockscreen.
lockscreenToGlanceableHubTransitionViewModel.notificationTranslationX,
glanceableHubToLockscreenTransitionViewModel.notificationTranslationX,
+ if (SceneContainerFlag.isEnabled) {
+ // The container may need to be translated along the X axis as the unfolded
+ // foldable is folded slightly.
+ unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = false)
+ } else {
+ emptyFlow()
+ }
)
.dumpWhileCollecting("translationX")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
index cb360fe..6acb12a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
@@ -17,18 +17,18 @@
package com.android.systemui.statusbar.notification.ui.viewbinder
import android.util.Log
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
-private const val TAG = "HunBinder"
-private val DEBUG = true // Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG)
-
class HeadsUpNotificationViewBinder
@Inject
constructor(private val viewModel: NotificationListViewModel) {
@@ -39,10 +39,6 @@
viewModel.pinnedHeadsUpRows
.sample(viewModel.headsUpAnimationsEnabled, ::Pair)
.collect { (newKeys, animationsEnabled) ->
- if (DEBUG) {
- Log.d(TAG, "update:$newKeys")
- }
-
val added = newKeys - previousKeys
val removed = previousKeys - newKeys
previousKeys = newKeys
@@ -70,9 +66,19 @@
launch {
viewModel.hasPinnedHeadsUpRow.collect { parentView.setInHeadsUpPinnedMode(it) }
}
+ launch {
+ parentView.isHeadsUpAnimatingAway.collect { viewModel.setHeadsUpAnimatingAway(it) }
+ }
}
private fun obtainView(key: HeadsUpRowKey): ExpandableNotificationRow {
return viewModel.elementKeyFor(key) as ExpandableNotificationRow
}
}
+
+private val NotificationStackScrollLayout.isHeadsUpAnimatingAway: Flow<Boolean>
+ get() =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ setHeadsUpAnimatingAwayListener { animatingAway -> trySend(animatingAway) }
+ awaitClose { setHeadsUpAnimatingAwayListener(null) }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 37646ae..6546db9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -14,51 +14,19 @@
package com.android.systemui.statusbar.phone
-import android.app.ActivityManager
-import android.app.ActivityOptions
-import android.app.ActivityTaskManager
import android.app.PendingIntent
-import android.app.TaskStackBuilder
-import android.content.Context
import android.content.Intent
import android.os.Bundle
-import android.os.RemoteException
import android.os.UserHandle
-import android.provider.Settings
-import android.util.Log
-import android.view.RemoteAnimationAdapter
import android.view.View
-import android.view.WindowManager
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.ActivityIntentHelper
import com.android.systemui.animation.ActivityTransitionAnimator
-import com.android.systemui.animation.ActivityTransitionAnimator.PendingIntentStarter
-import com.android.systemui.animation.DelegateTransitionAnimatorController
-import com.android.systemui.assist.AssistManager
-import com.android.systemui.camera.CameraIntents.Companion.isInsecureCameraIntent
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.DisplayId
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.KeyguardViewMediator
-import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
-import com.android.systemui.res.R
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.shade.ShadeController
-import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
-import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
-import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.kotlin.getOrNull
import dagger.Lazy
-import java.util.Optional
import javax.inject.Inject
/** Handles start activity logic in SystemUI. */
@@ -66,38 +34,12 @@
class ActivityStarterImpl
@Inject
constructor(
- private val centralSurfacesOptLazy: Lazy<Optional<CentralSurfaces>>,
- private val assistManagerLazy: Lazy<AssistManager>,
- private val dozeServiceHostLazy: Lazy<DozeServiceHost>,
- private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>,
- private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
- private val shadeControllerLazy: Lazy<ShadeController>,
- private val commandQueue: CommandQueue,
- private val shadeAnimationInteractor: ShadeAnimationInteractor,
- private val statusBarKeyguardViewManagerLazy: Lazy<StatusBarKeyguardViewManager>,
- private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>,
- private val activityTransitionAnimator: ActivityTransitionAnimator,
- private val context: Context,
- @DisplayId private val displayId: Int,
- private val lockScreenUserManager: NotificationLockscreenUserManager,
- private val statusBarWindowController: StatusBarWindowController,
- private val wakefulnessLifecycle: WakefulnessLifecycle,
- private val keyguardStateController: KeyguardStateController,
private val statusBarStateController: SysuiStatusBarStateController,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val deviceProvisionedController: DeviceProvisionedController,
- private val userTracker: UserTracker,
- private val activityIntentHelper: ActivityIntentHelper,
@Main private val mainExecutor: DelayableExecutor,
+ legacyActivityStarter: Lazy<LegacyActivityStarterInternalImpl>
) : ActivityStarter {
- companion object {
- const val TAG = "ActivityStarterImpl"
- }
- private val centralSurfaces: CentralSurfaces?
- get() = centralSurfacesOptLazy.get().getOrNull()
-
- private val activityStarterInternal = ActivityStarterInternal()
+ private val activityStarterInternal: ActivityStarterInternal = legacyActivityStarter.get()
override fun startPendingIntentDismissingKeyguard(intent: PendingIntent) {
activityStarterInternal.startPendingIntentDismissingKeyguard(intent = intent)
@@ -401,575 +343,11 @@
}
}
+ override fun shouldAnimateLaunch(isActivityIntent: Boolean): Boolean {
+ return activityStarterInternal.shouldAnimateLaunch(isActivityIntent)
+ }
+
private fun postOnUiThread(delay: Int = 0, runnable: Runnable) {
mainExecutor.executeDelayed(runnable, delay.toLong())
}
-
- /**
- * Whether we should animate an activity launch.
- *
- * Note: This method must be called *before* dismissing the keyguard.
- */
- private fun shouldAnimateLaunch(
- isActivityIntent: Boolean,
- showOverLockscreen: Boolean,
- ): Boolean {
- // TODO(b/294418322): Support launch animations when occluded.
- if (keyguardStateController.isOccluded) {
- return false
- }
-
- // Always animate if we are not showing the keyguard or if we animate over the lockscreen
- // (without unlocking it).
- if (showOverLockscreen || !keyguardStateController.isShowing) {
- return true
- }
-
- // We don't animate non-activity launches as they can break the animation.
- // TODO(b/184121838): Support non activity launches on the lockscreen.
- return isActivityIntent
- }
-
- override fun shouldAnimateLaunch(isActivityIntent: Boolean): Boolean {
- return shouldAnimateLaunch(isActivityIntent, false)
- }
-
- /**
- * Encapsulates the activity logic for activity starter.
- *
- * Logic is duplicated in {@link CentralSurfacesImpl}
- */
- private inner class ActivityStarterInternal {
- /** Starts an activity after dismissing keyguard. */
- fun startActivityDismissingKeyguard(
- intent: Intent,
- onlyProvisioned: Boolean = false,
- dismissShade: Boolean = false,
- disallowEnterPictureInPictureWhileLaunching: Boolean = false,
- callback: ActivityStarter.Callback? = null,
- flags: Int = 0,
- animationController: ActivityTransitionAnimator.Controller? = null,
- userHandle: UserHandle? = null,
- customMessage: String? = null,
- ) {
- val userHandle: UserHandle = userHandle ?: getActivityUserHandle(intent)
-
- if (onlyProvisioned && !deviceProvisionedController.isDeviceProvisioned) return
-
- val willLaunchResolverActivity: Boolean =
- activityIntentHelper.wouldLaunchResolverActivity(
- intent,
- lockScreenUserManager.currentUserId
- )
-
- val animate =
- animationController != null &&
- !willLaunchResolverActivity &&
- shouldAnimateLaunch(isActivityIntent = true)
- val animController =
- wrapAnimationControllerForShadeOrStatusBar(
- animationController = animationController,
- dismissShade = dismissShade,
- isLaunchForActivity = true,
- )
-
- // If we animate, we will dismiss the shade only once the animation is done. This is
- // taken care of by the StatusBarLaunchAnimationController.
- val dismissShadeDirectly = dismissShade && animController == null
-
- val runnable = Runnable {
- assistManagerLazy.get().hideAssist()
- intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
- intent.addFlags(flags)
- val result = intArrayOf(ActivityManager.START_CANCELED)
- activityTransitionAnimator.startIntentWithAnimation(
- animController,
- animate,
- intent.getPackage()
- ) { adapter: RemoteAnimationAdapter? ->
- val options =
- ActivityOptions(CentralSurfaces.getActivityOptions(displayId, adapter))
-
- // We know that the intent of the caller is to dismiss the keyguard and
- // this runnable is called right after the keyguard is solved, so we tell
- // WM that we should dismiss it to avoid flickers when opening an activity
- // that can also be shown over the keyguard.
- options.setDismissKeyguardIfInsecure()
- options.setDisallowEnterPictureInPictureWhileLaunching(
- disallowEnterPictureInPictureWhileLaunching
- )
- if (isInsecureCameraIntent(intent)) {
- // Normally an activity will set it's requested rotation
- // animation on its window. However when launching an activity
- // causes the orientation to change this is too late. In these cases
- // the default animation is used. This doesn't look good for
- // the camera (as it rotates the camera contents out of sync
- // with physical reality). So, we ask the WindowManager to
- // force the cross fade animation if an orientation change
- // happens to occur during the launch.
- options.rotationAnimationHint =
- WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
- }
- if (Settings.Panel.ACTION_VOLUME == intent.action) {
- // Settings Panel is implemented as activity(not a dialog), so
- // underlying app is paused and may enter picture-in-picture mode
- // as a result.
- // So we need to disable picture-in-picture mode here
- // if it is volume panel.
- options.setDisallowEnterPictureInPictureWhileLaunching(true)
- }
- try {
- result[0] =
- ActivityTaskManager.getService()
- .startActivityAsUser(
- null,
- context.basePackageName,
- context.attributionTag,
- intent,
- intent.resolveTypeIfNeeded(context.contentResolver),
- null,
- null,
- 0,
- Intent.FLAG_ACTIVITY_NEW_TASK,
- null,
- options.toBundle(),
- userHandle.identifier,
- )
- } catch (e: RemoteException) {
- Log.w(TAG, "Unable to start activity", e)
- }
- result[0]
- }
- callback?.onActivityStarted(result[0])
- }
- val cancelRunnable = Runnable {
- callback?.onActivityStarted(ActivityManager.START_CANCELED)
- }
- // Do not deferKeyguard when occluded because, when keyguard is occluded,
- // we do not launch the activity until keyguard is done.
- val occluded = (keyguardStateController.isShowing && keyguardStateController.isOccluded)
- val deferred = !occluded
- executeRunnableDismissingKeyguard(
- runnable,
- cancelRunnable,
- dismissShadeDirectly,
- willLaunchResolverActivity,
- deferred,
- animate,
- customMessage,
- )
- }
-
- /**
- * Starts a pending intent after dismissing keyguard.
- *
- * This can be called in a background thread (to prevent calls in [ActivityIntentHelper] in
- * the main thread).
- */
- fun startPendingIntentDismissingKeyguard(
- intent: PendingIntent,
- intentSentUiThreadCallback: Runnable? = null,
- associatedView: View? = null,
- animationController: ActivityTransitionAnimator.Controller? = null,
- showOverLockscreen: Boolean = false,
- fillInIntent: Intent? = null,
- extraOptions: Bundle? = null,
- ) {
- val animationController =
- if (associatedView is ExpandableNotificationRow) {
- centralSurfaces?.getAnimatorControllerFromNotification(associatedView)
- } else animationController
-
- val willLaunchResolverActivity =
- (intent.isActivity &&
- activityIntentHelper.wouldPendingLaunchResolverActivity(
- intent,
- lockScreenUserManager.currentUserId,
- ))
-
- val actuallyShowOverLockscreen =
- showOverLockscreen &&
- intent.isActivity &&
- activityIntentHelper.wouldPendingShowOverLockscreen(
- intent,
- lockScreenUserManager.currentUserId
- )
-
- val animate =
- !willLaunchResolverActivity &&
- animationController != null &&
- shouldAnimateLaunch(intent.isActivity, actuallyShowOverLockscreen)
-
- // We wrap animationCallback with a StatusBarLaunchAnimatorController so
- // that the shade is collapsed after the animation (or when it is cancelled,
- // aborted, etc).
- val statusBarController =
- wrapAnimationControllerForShadeOrStatusBar(
- animationController = animationController,
- dismissShade = true,
- isLaunchForActivity = intent.isActivity,
- )
- val controller =
- if (actuallyShowOverLockscreen) {
- wrapAnimationControllerForLockscreen(statusBarController)
- } else {
- statusBarController
- }
-
- // If we animate, don't collapse the shade and defer the keyguard dismiss (in case we
- // run the animation on the keyguard). The animation will take care of (instantly)
- // collapsing the shade and hiding the keyguard once it is done.
- val collapse = !animate
- val runnable = Runnable {
- try {
- activityTransitionAnimator.startPendingIntentWithAnimation(
- controller,
- animate,
- intent.creatorPackage,
- actuallyShowOverLockscreen,
- object : PendingIntentStarter {
- override fun startPendingIntent(
- animationAdapter: RemoteAnimationAdapter?
- ): Int {
- val options =
- ActivityOptions(
- CentralSurfaces.getActivityOptions(
- displayId,
- animationAdapter
- )
- .apply { extraOptions?.let { putAll(it) } }
- )
- // TODO b/221255671: restrict this to only be set for
- // notifications
- options.isEligibleForLegacyPermissionPrompt = true
- options.setPendingIntentBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
- )
- return intent.sendAndReturnResult(
- context,
- 0,
- fillInIntent,
- null,
- null,
- null,
- options.toBundle()
- )
- }
- },
- )
- } catch (e: PendingIntent.CanceledException) {
- // the stack trace isn't very helpful here.
- // Just log the exception message.
- Log.w(TAG, "Sending intent failed: $e")
- if (!collapse) {
- // executeRunnableDismissingKeyguard did not collapse for us already.
- shadeControllerLazy.get().collapseOnMainThread()
- }
- // TODO: Dismiss Keyguard.
- }
- if (intent.isActivity) {
- assistManagerLazy.get().hideAssist()
- // This activity could have started while the device is dreaming, in which case
- // the dream would occlude the activity. In order to show the newly started
- // activity, we wake from the dream.
- keyguardUpdateMonitor.awakenFromDream()
- }
- intentSentUiThreadCallback?.let { postOnUiThread(runnable = it) }
- }
-
- if (!actuallyShowOverLockscreen) {
- postOnUiThread(delay = 0) {
- executeRunnableDismissingKeyguard(
- runnable = runnable,
- afterKeyguardGone = willLaunchResolverActivity,
- dismissShade = collapse,
- willAnimateOnKeyguard = animate,
- )
- }
- } else {
- postOnUiThread(delay = 0, runnable)
- }
- }
-
- /** Starts an Activity. */
- fun startActivity(
- intent: Intent,
- dismissShade: Boolean = false,
- animationController: ActivityTransitionAnimator.Controller? = null,
- showOverLockscreenWhenLocked: Boolean = false,
- userHandle: UserHandle? = null,
- ) {
- val userHandle = userHandle ?: getActivityUserHandle(intent)
- // Make sure that we dismiss the keyguard if it is directly dismissible or when we don't
- // want to show the activity above it.
- if (keyguardStateController.isUnlocked || !showOverLockscreenWhenLocked) {
- startActivityDismissingKeyguard(
- intent = intent,
- onlyProvisioned = false,
- dismissShade = dismissShade,
- disallowEnterPictureInPictureWhileLaunching = false,
- callback = null,
- flags = 0,
- animationController = animationController,
- userHandle = userHandle,
- )
- return
- }
-
- val animate =
- animationController != null &&
- shouldAnimateLaunch(
- /* isActivityIntent= */ true,
- showOverLockscreenWhenLocked
- ) == true
-
- var controller: ActivityTransitionAnimator.Controller? = null
- if (animate) {
- // Wrap the animation controller to dismiss the shade and set
- // mIsLaunchingActivityOverLockscreen during the animation.
- val delegate =
- wrapAnimationControllerForShadeOrStatusBar(
- animationController = animationController,
- dismissShade = dismissShade,
- isLaunchForActivity = true,
- )
- controller = wrapAnimationControllerForLockscreen(delegate)
- } else if (dismissShade) {
- // The animation will take care of dismissing the shade at the end of the animation.
- // If we don't animate, collapse it directly.
- shadeControllerLazy.get().cancelExpansionAndCollapseShade()
- }
-
- // We should exit the dream to prevent the activity from starting below the
- // dream.
- if (keyguardUpdateMonitor.isDreaming) {
- centralSurfaces?.awakenDreams()
- }
-
- activityTransitionAnimator.startIntentWithAnimation(
- controller,
- animate,
- intent.getPackage(),
- showOverLockscreenWhenLocked
- ) { adapter: RemoteAnimationAdapter? ->
- TaskStackBuilder.create(context)
- .addNextIntent(intent)
- .startActivities(
- CentralSurfaces.getActivityOptions(displayId, adapter),
- userHandle
- )
- }
- }
-
- /** Executes an action after dismissing keyguard. */
- fun dismissKeyguardThenExecute(
- action: OnDismissAction,
- cancel: Runnable? = null,
- afterKeyguardGone: Boolean = false,
- customMessage: String? = null,
- ) {
- if (
- !action.willRunAnimationOnKeyguard() &&
- wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_ASLEEP &&
- keyguardStateController.canDismissLockScreen() &&
- !statusBarStateController.leaveOpenOnKeyguardHide() &&
- dozeServiceHostLazy.get().isPulsing
- ) {
- // Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a
- // pulse.
- // TODO: Factor this transition out of BiometricUnlockController.
- biometricUnlockControllerLazy
- .get()
- .startWakeAndUnlock(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
- }
- if (keyguardStateController.isShowing) {
- statusBarKeyguardViewManagerLazy
- .get()
- .dismissWithAction(action, cancel, afterKeyguardGone, customMessage)
- } else {
- // If the keyguard isn't showing but the device is dreaming, we should exit the
- // dream.
- if (keyguardUpdateMonitor.isDreaming) {
- centralSurfaces?.awakenDreams()
- }
- action.onDismiss()
- }
- }
-
- /** Executes an action after dismissing keyguard. */
- fun executeRunnableDismissingKeyguard(
- runnable: Runnable? = null,
- cancelAction: Runnable? = null,
- dismissShade: Boolean = false,
- afterKeyguardGone: Boolean = false,
- deferred: Boolean = false,
- willAnimateOnKeyguard: Boolean = false,
- customMessage: String? = null,
- ) {
- val onDismissAction: OnDismissAction =
- object : OnDismissAction {
- override fun onDismiss(): Boolean {
- if (runnable != null) {
- if (
- keyguardStateController.isShowing &&
- keyguardStateController.isOccluded
- ) {
- statusBarKeyguardViewManagerLazy
- .get()
- .addAfterKeyguardGoneRunnable(runnable)
- } else {
- mainExecutor.execute(runnable)
- }
- }
- if (dismissShade) {
- shadeControllerLazy.get().collapseShadeForActivityStart()
- }
- return deferred
- }
-
- override fun willRunAnimationOnKeyguard(): Boolean {
- return willAnimateOnKeyguard
- }
- }
- dismissKeyguardThenExecute(
- onDismissAction,
- cancelAction,
- afterKeyguardGone,
- customMessage,
- )
- }
-
- /**
- * Return a [ActivityTransitionAnimator.Controller] wrapping `animationController` so that:
- * - if it launches in the notification shade window and `dismissShade` is true, then the
- * shade will be instantly dismissed at the end of the animation.
- * - if it launches in status bar window, it will make the status bar window match the
- * device size during the animation (that way, the animation won't be clipped by the
- * status bar size).
- *
- * @param animationController the controller that is wrapped and will drive the main
- * animation.
- * @param dismissShade whether the notification shade will be dismissed at the end of the
- * animation. This is ignored if `animationController` is not animating in the shade
- * window.
- * @param isLaunchForActivity whether the launch is for an activity.
- */
- private fun wrapAnimationControllerForShadeOrStatusBar(
- animationController: ActivityTransitionAnimator.Controller?,
- dismissShade: Boolean,
- isLaunchForActivity: Boolean,
- ): ActivityTransitionAnimator.Controller? {
- if (animationController == null) {
- return null
- }
- val rootView = animationController.transitionContainer.rootView
- val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> =
- statusBarWindowController.wrapAnimationControllerIfInStatusBar(
- rootView,
- animationController
- )
- if (controllerFromStatusBar.isPresent) {
- return controllerFromStatusBar.get()
- }
-
- centralSurfaces?.let {
- // If the view is not in the status bar, then we are animating a view in the shade.
- // We have to make sure that we collapse it when the animation ends or is cancelled.
- if (dismissShade) {
- return StatusBarTransitionAnimatorController(
- animationController,
- shadeAnimationInteractor,
- shadeControllerLazy.get(),
- notifShadeWindowControllerLazy.get(),
- commandQueue,
- displayId,
- isLaunchForActivity
- )
- }
- }
-
- return animationController
- }
-
- /**
- * Wraps an animation controller so that if an activity would be launched on top of the
- * lockscreen, the correct flags are set for it to be occluded.
- */
- private fun wrapAnimationControllerForLockscreen(
- animationController: ActivityTransitionAnimator.Controller?
- ): ActivityTransitionAnimator.Controller? {
- return animationController?.let {
- object : DelegateTransitionAnimatorController(it) {
- override fun onIntentStarted(willAnimate: Boolean) {
- delegate.onIntentStarted(willAnimate)
- if (willAnimate) {
- centralSurfaces?.setIsLaunchingActivityOverLockscreen(true)
- }
- }
-
- override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
- super.onTransitionAnimationStart(isExpandingFullyAbove)
-
- // Double check that the keyguard is still showing and not going
- // away, but if so set the keyguard occluded. Typically, WM will let
- // KeyguardViewMediator know directly, but we're overriding that to
- // play the custom launch animation, so we need to take care of that
- // here. The unocclude animation is not overridden, so WM will call
- // KeyguardViewMediator's unocclude animation runner when the
- // activity is exited.
- if (
- keyguardStateController.isShowing &&
- !keyguardStateController.isKeyguardGoingAway
- ) {
- Log.d(TAG, "Setting occluded = true in #startActivity.")
- keyguardViewMediatorLazy
- .get()
- .setOccluded(true /* isOccluded */, true /* animate */)
- }
- }
-
- override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
- // Set mIsLaunchingActivityOverLockscreen to false before actually
- // finishing the animation so that we can assume that
- // mIsLaunchingActivityOverLockscreen being true means that we will
- // collapse the shade (or at least run the post collapse runnables)
- // later on.
- centralSurfaces?.setIsLaunchingActivityOverLockscreen(false)
- delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
- }
-
- override fun onTransitionAnimationCancelled(
- newKeyguardOccludedState: Boolean?
- ) {
- if (newKeyguardOccludedState != null) {
- keyguardViewMediatorLazy
- .get()
- .setOccluded(newKeyguardOccludedState, false /* animate */)
- }
-
- // Set mIsLaunchingActivityOverLockscreen to false before actually
- // finishing the animation so that we can assume that
- // mIsLaunchingActivityOverLockscreen being true means that we will
- // collapse the shade (or at least run the // post collapse
- // runnables) later on.
- centralSurfaces?.setIsLaunchingActivityOverLockscreen(false)
- delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
- }
- }
- }
- }
-
- /** Retrieves the current user handle to start the Activity. */
- private fun getActivityUserHandle(intent: Intent): UserHandle {
- val packages: Array<String> =
- context.resources.getStringArray(R.array.system_ui_packages)
- for (pkg in packages) {
- val componentName = intent.component ?: break
- if (pkg == componentName.packageName) {
- return UserHandle(UserHandle.myUserId())
- }
- }
- return userTracker.userHandle
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
new file mode 100644
index 0000000..e844398
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.os.Bundle
+import android.os.UserHandle
+import android.view.View
+import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.plugins.ActivityStarter
+
+interface ActivityStarterInternal {
+ /**
+ * Starts a pending intent after dismissing keyguard.
+ *
+ * This can be called in a background thread (to prevent calls in [ActivityIntentHelper] in the
+ * main thread).
+ */
+ fun startPendingIntentDismissingKeyguard(
+ intent: PendingIntent,
+ intentSentUiThreadCallback: Runnable? = null,
+ associatedView: View? = null,
+ animationController: ActivityTransitionAnimator.Controller? = null,
+ showOverLockscreen: Boolean = false,
+ fillInIntent: Intent? = null,
+ extraOptions: Bundle? = null,
+ )
+
+ /** Starts an activity after dismissing keyguard. */
+ fun startActivityDismissingKeyguard(
+ intent: Intent,
+ dismissShade: Boolean,
+ onlyProvisioned: Boolean = false,
+ callback: ActivityStarter.Callback? = null,
+ flags: Int = 0,
+ animationController: ActivityTransitionAnimator.Controller? = null,
+ customMessage: String? = null,
+ disallowEnterPictureInPictureWhileLaunching: Boolean = false,
+ userHandle: UserHandle? = null,
+ )
+
+ /** Starts an Activity. */
+ fun startActivity(
+ intent: Intent,
+ dismissShade: Boolean,
+ animationController: ActivityTransitionAnimator.Controller?,
+ showOverLockscreenWhenLocked: Boolean,
+ userHandle: UserHandle? = null,
+ )
+
+ /** Executes an action after dismissing keyguard. */
+ fun dismissKeyguardThenExecute(
+ action: ActivityStarter.OnDismissAction,
+ cancel: Runnable?,
+ afterKeyguardGone: Boolean,
+ customMessage: String? = null,
+ )
+
+ /** Executes an action after dismissing keyguard. */
+ fun executeRunnableDismissingKeyguard(
+ runnable: Runnable?,
+ cancelAction: Runnable? = null,
+ dismissShade: Boolean = false,
+ afterKeyguardGone: Boolean = false,
+ deferred: Boolean = false,
+ willAnimateOnKeyguard: Boolean = false,
+ customMessage: String? = null,
+ )
+
+ /** Whether we should animate an activity launch. */
+ fun shouldAnimateLaunch(isActivityIntent: Boolean): Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
new file mode 100644
index 0000000..c101755
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.os.Bundle
+import android.os.UserHandle
+import android.view.View
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Inject
+
+/**
+ * Encapsulates the activity logic for activity starter when flexiglass is enabled.
+ *
+ * TODO: b/308819693
+ */
+@SysUISingleton
+class ActivityStarterInternalImpl @Inject constructor() : ActivityStarterInternal {
+ override fun startPendingIntentDismissingKeyguard(
+ intent: PendingIntent,
+ intentSentUiThreadCallback: Runnable?,
+ associatedView: View?,
+ animationController: ActivityTransitionAnimator.Controller?,
+ showOverLockscreen: Boolean,
+ fillInIntent: Intent?,
+ extraOptions: Bundle?
+ ) {
+ TODO("Not yet implemented b/308819693")
+ }
+
+ override fun startActivityDismissingKeyguard(
+ intent: Intent,
+ dismissShade: Boolean,
+ onlyProvisioned: Boolean,
+ callback: ActivityStarter.Callback?,
+ flags: Int,
+ animationController: ActivityTransitionAnimator.Controller?,
+ customMessage: String?,
+ disallowEnterPictureInPictureWhileLaunching: Boolean,
+ userHandle: UserHandle?
+ ) {
+ TODO("Not yet implemented b/308819693")
+ }
+
+ override fun startActivity(
+ intent: Intent,
+ dismissShade: Boolean,
+ animationController: ActivityTransitionAnimator.Controller?,
+ showOverLockscreenWhenLocked: Boolean,
+ userHandle: UserHandle?
+ ) {
+ TODO("Not yet implemented b/308819693")
+ }
+
+ override fun dismissKeyguardThenExecute(
+ action: ActivityStarter.OnDismissAction,
+ cancel: Runnable?,
+ afterKeyguardGone: Boolean,
+ customMessage: String?
+ ) {
+ TODO("Not yet implemented b/308819693")
+ }
+
+ override fun executeRunnableDismissingKeyguard(
+ runnable: Runnable?,
+ cancelAction: Runnable?,
+ dismissShade: Boolean,
+ afterKeyguardGone: Boolean,
+ deferred: Boolean,
+ willAnimateOnKeyguard: Boolean,
+ customMessage: String?
+ ) {
+ TODO("Not yet implemented b/308819693")
+ }
+
+ override fun shouldAnimateLaunch(isActivityIntent: Boolean): Boolean {
+ TODO("Not yet implemented b/308819693")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 8b7b348..79e6a0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -67,6 +67,8 @@
import dagger.Lazy;
+import kotlinx.coroutines.ExperimentalCoroutinesApi;
+
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -77,8 +79,6 @@
import javax.inject.Inject;
-import kotlinx.coroutines.ExperimentalCoroutinesApi;
-
/**
* Controller which coordinates all the biometric unlocking actions with the UI.
*/
@@ -183,6 +183,7 @@
private final SystemClock mSystemClock;
private final boolean mOrderUnlockAndWake;
private final Lazy<SelectedUserInteractor> mSelectedUserInteractor;
+ private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private long mLastFpFailureUptimeMillis;
private int mNumConsecutiveFpFailures;
@@ -323,6 +324,7 @@
mOrderUnlockAndWake = resources.getBoolean(
com.android.internal.R.bool.config_orderUnlockAndWake);
mSelectedUserInteractor = selectedUserInteractor;
+ mKeyguardTransitionInteractor = keyguardTransitionInteractor;
javaAdapter.alwaysCollectFlow(
keyguardTransitionInteractor.getStartedKeyguardTransitionStep(),
this::consumeTransitionStepOnStartedKeyguardState);
@@ -665,7 +667,8 @@
}
if (isKeyguardShowing) {
if ((mKeyguardViewController.primaryBouncerIsOrWillBeShowing()
- || mKeyguardBypassController.getAltBouncerShowing()) && unlockingAllowed) {
+ || mKeyguardTransitionInteractor.getCurrentState()
+ == KeyguardState.ALTERNATE_BOUNCER) && unlockingAllowed) {
return MODE_DISMISS_BOUNCER;
} else if (unlockingAllowed && bypass) {
return MODE_UNLOCK_COLLAPSING;
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 1d6b744..cb3c03e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -164,7 +164,6 @@
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -592,9 +591,6 @@
private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
(extractor, which) -> updateTheme();
-
- private final SceneContainerFlags mSceneContainerFlags;
-
private final BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor;
/**
@@ -708,7 +704,6 @@
UserTracker userTracker,
Provider<FingerprintManager> fingerprintManager,
ActivityStarter activityStarter,
- SceneContainerFlags sceneContainerFlags,
BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor
) {
mContext = context;
@@ -804,7 +799,6 @@
mUserTracker = userTracker;
mFingerprintManager = fingerprintManager;
mActivityStarter = activityStarter;
- mSceneContainerFlags = sceneContainerFlags;
mBrightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
@@ -1088,7 +1082,7 @@
mJavaAdapter.alwaysCollectFlow(
mCommunalInteractor.isIdleOnCommunal(),
mIdleOnCommunalConsumer);
- if (mSceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
mJavaAdapter.alwaysCollectFlow(
mBrightnessMirrorShowingInteractor.isShowing(),
this::setBrightnessMirrorShowing
@@ -1277,7 +1271,7 @@
// Set up the quick settings tile panel
final View container = getNotificationShadeWindowView().findViewById(R.id.qs_frame);
- if (container != null && !mSceneContainerFlags.isEnabled()) {
+ if (container != null && !SceneContainerFlag.isEnabled()) {
FragmentHostManager fragmentHostManager =
mFragmentService.getFragmentHostManager(container);
ExtensionFragmentListener.attachExtensonToFragment(
@@ -1545,8 +1539,7 @@
mShadeSurface,
mShadeExpansionStateManager,
mBiometricUnlockController,
- mStackScroller,
- mKeyguardBypassController);
+ mStackScroller);
mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
mKeyguardIndicationController
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 3f200d5..8ec8d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -91,7 +91,8 @@
StateFlowKt.MutableStateFlow(null);
private final MutableStateFlow<Set<HeadsUpRowRepository>> mHeadsUpNotificationRows =
StateFlowKt.MutableStateFlow(new HashSet<>());
- private final MutableStateFlow<Boolean> mHeadsUpGoingAway = StateFlowKt.MutableStateFlow(false);
+ private final MutableStateFlow<Boolean> mHeadsUpAnimatingAway =
+ StateFlowKt.MutableStateFlow(false);
private boolean mReleaseOnExpandFinish;
private boolean mTrackingHeadsUp;
private final HashSet<String> mSwipedOutKeys = new HashSet<>();
@@ -167,7 +168,10 @@
updateResources();
}
});
- javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(), this::onShadeOrQsExpanded);
+ if (!NotificationsHeadsUpRefactor.isEnabled()) {
+ javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(),
+ this::onShadeOrQsExpanded);
+ }
}
public void setAnimationStateHandler(AnimationStateHandler handler) {
@@ -184,7 +188,7 @@
// Public methods:
/**
- * Add a listener to receive callbacks onHeadsUpGoingAway
+ * Add a listener to receive callbacks {@link #setHeadsUpAnimatingAway(boolean)}
*/
@Override
public void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) {
@@ -261,10 +265,11 @@
}
private void onShadeOrQsExpanded(Boolean isExpanded) {
+ NotificationsHeadsUpRefactor.assertInLegacyMode();
if (isExpanded != mIsExpanded) {
mIsExpanded = isExpanded;
if (isExpanded) {
- mHeadsUpGoingAway.setValue(false);
+ mHeadsUpAnimatingAway.setValue(false);
}
}
}
@@ -274,20 +279,15 @@
* animating out. This is used to keep the touchable regions in a reasonable state.
*/
@Override
- public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
- if (headsUpGoingAway != mHeadsUpGoingAway.getValue()) {
+ public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
+ if (headsUpAnimatingAway != mHeadsUpAnimatingAway.getValue()) {
for (OnHeadsUpPhoneListenerChange listener : mHeadsUpPhoneListeners) {
- listener.onHeadsUpGoingAwayStateChanged(headsUpGoingAway);
+ listener.onHeadsUpAnimatingAwayStateChanged(headsUpAnimatingAway);
}
- mHeadsUpGoingAway.setValue(headsUpGoingAway);
+ mHeadsUpAnimatingAway.setValue(headsUpAnimatingAway);
}
}
- @Override
- public boolean isHeadsUpGoingAway() {
- return mHeadsUpGoingAway.getValue();
- }
-
/**
* Notifies that a remote input textbox in notification gets active or inactive.
*
@@ -504,8 +504,13 @@
@Override
@NonNull
- public Flow<Boolean> getHeadsUpAnimatingAway() {
- return mHeadsUpGoingAway;
+ public StateFlow<Boolean> isHeadsUpAnimatingAway() {
+ return mHeadsUpAnimatingAway;
+ }
+
+ @Override
+ public boolean isHeadsUpAnimatingAwayValue() {
+ return mHeadsUpAnimatingAway.getValue();
}
///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index a3d316b..a8941bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.phone
import android.annotation.IntDef
-import android.content.Context
import android.content.pm.PackageManager
+import android.content.res.Resources
import android.hardware.biometrics.BiometricSourceType
import android.provider.Settings
import androidx.annotation.VisibleForTesting
@@ -26,7 +26,10 @@
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.shade.data.repository.ShadeRepository
@@ -46,12 +49,20 @@
import javax.inject.Inject
@SysUISingleton
-open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassController {
+class KeyguardBypassController @Inject constructor(
+ @Main resources: Resources,
+ packageManager: PackageManager,
+ @Application applicationScope: CoroutineScope,
+ tunerService: TunerService,
+ private val statusBarStateController: StatusBarStateController,
+ lockscreenUserManager: NotificationLockscreenUserManager,
+ private val keyguardStateController: KeyguardStateController,
+ private val shadeRepository: ShadeRepository,
+ devicePostureController: DevicePostureController,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ dumpManager: DumpManager
+) : Dumpable, StackScrollAlgorithm.BypassController {
- private val mKeyguardStateController: KeyguardStateController
- private val statusBarStateController: StatusBarStateController
- private val shadeRepository: ShadeRepository
- private val devicePostureController: DevicePostureController
@BypassOverride private val bypassOverride: Int
private var hasFaceFeature: Boolean
@DevicePostureInt private val configFaceAuthSupportedPosture: Int
@@ -99,7 +110,7 @@
FACE_UNLOCK_BYPASS_NEVER -> false
else -> field
}
- return enabled && mKeyguardStateController.isFaceEnrolledAndEnabled &&
+ return enabled && keyguardStateController.isFaceEnrolledAndEnabled &&
isPostureAllowedForFaceAuth()
}
private set(value) {
@@ -108,70 +119,44 @@
}
var bouncerShowing: Boolean = false
- var altBouncerShowing: Boolean = false
var launchingAffordance: Boolean = false
var qsExpanded = false
- @Inject
- constructor(
- context: Context,
- @Application applicationScope: CoroutineScope,
- tunerService: TunerService,
- statusBarStateController: StatusBarStateController,
- lockscreenUserManager: NotificationLockscreenUserManager,
- keyguardStateController: KeyguardStateController,
- shadeRepository: ShadeRepository,
- devicePostureController: DevicePostureController,
- dumpManager: DumpManager
- ) {
- this.mKeyguardStateController = keyguardStateController
- this.statusBarStateController = statusBarStateController
- this.shadeRepository = shadeRepository
- this.devicePostureController = devicePostureController
-
- bypassOverride = context.resources.getInteger(R.integer.config_face_unlock_bypass_override)
+ init {
+ bypassOverride = resources.getInteger(R.integer.config_face_unlock_bypass_override)
configFaceAuthSupportedPosture =
- context.resources.getInteger(R.integer.config_face_auth_supported_posture)
-
- hasFaceFeature = context.packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)
- if (!hasFaceFeature) {
- return
- }
-
-
- if (configFaceAuthSupportedPosture != DEVICE_POSTURE_UNKNOWN) {
- devicePostureController.addCallback { posture ->
- if (postureState != posture) {
- postureState = posture
- notifyListeners()
+ resources.getInteger(R.integer.config_face_auth_supported_posture)
+ hasFaceFeature = packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)
+ if (hasFaceFeature) {
+ if (configFaceAuthSupportedPosture != DEVICE_POSTURE_UNKNOWN) {
+ devicePostureController.addCallback { posture ->
+ if (postureState != posture) {
+ postureState = posture
+ notifyListeners()
+ }
}
}
- }
-
- dumpManager.registerNormalDumpable("KeyguardBypassController", this)
- statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
- override fun onStateChanged(newState: Int) {
- if (newState != StatusBarState.KEYGUARD) {
- pendingUnlock = null
- }
- }
- })
-
- listenForQsExpandedChange(applicationScope)
-
- val dismissByDefault = if (context.resources.getBoolean(
- com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) 1 else 0
-
- tunerService.addTunable({ key, _ ->
- bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0
- }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
-
- lockscreenUserManager.addUserChangedListener(
- object : NotificationLockscreenUserManager.UserChangedListener {
- override fun onUserChanged(userId: Int) {
- pendingUnlock = null
+ dumpManager.registerNormalDumpable("KeyguardBypassController", this)
+ statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
+ override fun onStateChanged(newState: Int) {
+ if (newState != StatusBarState.KEYGUARD) {
+ pendingUnlock = null
+ }
}
})
+ listenForQsExpandedChange(applicationScope)
+ val dismissByDefault = if (resources.getBoolean(
+ com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) 1 else 0
+ tunerService.addTunable({ key, _ ->
+ bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0
+ }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
+ lockscreenUserManager.addUserChangedListener(
+ object : NotificationLockscreenUserManager.UserChangedListener {
+ override fun onUserChanged(userId: Int) {
+ pendingUnlock = null
+ }
+ })
+ }
}
@VisibleForTesting
@@ -228,7 +213,8 @@
if (bypassEnabled) {
return when {
bouncerShowing -> true
- altBouncerShowing -> true
+ keyguardTransitionInteractor.getCurrentState() == KeyguardState.ALTERNATE_BOUNCER ->
+ true
statusBarStateController.state != StatusBarState.KEYGUARD -> false
launchingAffordance -> false
isPulseExpanding || qsExpanded -> false
@@ -260,7 +246,8 @@
pw.println(" bypassEnabled: $bypassEnabled")
pw.println(" canBypass: ${canBypass()}")
pw.println(" bouncerShowing: $bouncerShowing")
- pw.println(" altBouncerShowing: $altBouncerShowing")
+ pw.println(" altBouncerShowing:" +
+ " ${keyguardTransitionInteractor.getCurrentState() == KeyguardState.ALTERNATE_BOUNCER}")
pw.println(" isPulseExpanding: $isPulseExpanding")
pw.println(" launchingAffordance: $launchingAffordance")
pw.println(" qSExpanded: $qsExpanded")
@@ -273,7 +260,7 @@
val start = listeners.isEmpty()
listeners.add(listener)
if (start) {
- mKeyguardStateController.addCallback(faceAuthEnabledChangedCallback)
+ keyguardStateController.addCallback(faceAuthEnabledChangedCallback)
}
}
@@ -284,7 +271,7 @@
fun unregisterOnBypassStateChangedListener(listener: OnBypassStateChangedListener) {
listeners.remove(listener)
if (listeners.isEmpty()) {
- mKeyguardStateController.removeCallback(faceAuthEnabledChangedCallback)
+ keyguardStateController.removeCallback(faceAuthEnabledChangedCallback)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 38b3718..3343779 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -658,6 +658,7 @@
updateForHeadsUp(true);
}
+ // TODO(b/328579846) bind the StatusBar visibility to heads up events
void updateForHeadsUp(boolean animate) {
boolean showingKeyguardHeadsUp =
isKeyguardShowing() && mShadeViewStateProvider.shouldHeadsUpBeVisible();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
new file mode 100644
index 0000000..68d54e7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -0,0 +1,639 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.app.ActivityManager
+import android.app.ActivityOptions
+import android.app.ActivityTaskManager
+import android.app.PendingIntent
+import android.app.TaskStackBuilder
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.os.RemoteException
+import android.os.UserHandle
+import android.provider.Settings
+import android.util.Log
+import android.view.RemoteAnimationAdapter
+import android.view.View
+import android.view.WindowManager
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.DelegateTransitionAnimatorController
+import com.android.systemui.assist.AssistManager
+import com.android.systemui.camera.CameraIntents
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.DisplayId
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.KeyguardViewMediator
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeController
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.kotlin.getOrNull
+import dagger.Lazy
+import java.util.Optional
+import javax.inject.Inject
+
+/** Encapsulates the activity logic for activity starter. */
+@SysUISingleton
+class LegacyActivityStarterInternalImpl
+@Inject
+constructor(
+ private val centralSurfacesOptLazy: Lazy<Optional<CentralSurfaces>>,
+ private val keyguardStateController: KeyguardStateController,
+ private val statusBarStateController: SysuiStatusBarStateController,
+ private val assistManagerLazy: Lazy<AssistManager>,
+ private val dozeServiceHostLazy: Lazy<DozeServiceHost>,
+ private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>,
+ private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
+ private val shadeControllerLazy: Lazy<ShadeController>,
+ private val commandQueue: CommandQueue,
+ private val shadeAnimationInteractor: ShadeAnimationInteractor,
+ private val statusBarKeyguardViewManagerLazy: Lazy<StatusBarKeyguardViewManager>,
+ private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>,
+ private val activityTransitionAnimator: ActivityTransitionAnimator,
+ private val context: Context,
+ @DisplayId private val displayId: Int,
+ private val lockScreenUserManager: NotificationLockscreenUserManager,
+ private val statusBarWindowController: StatusBarWindowController,
+ private val wakefulnessLifecycle: WakefulnessLifecycle,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val deviceProvisionedController: DeviceProvisionedController,
+ private val userTracker: UserTracker,
+ private val activityIntentHelper: ActivityIntentHelper,
+ @Main private val mainExecutor: DelayableExecutor,
+) : ActivityStarterInternal {
+ private val centralSurfaces: CentralSurfaces?
+ get() = centralSurfacesOptLazy.get().getOrNull()
+
+ override fun startActivityDismissingKeyguard(
+ intent: Intent,
+ dismissShade: Boolean,
+ onlyProvisioned: Boolean,
+ callback: ActivityStarter.Callback?,
+ flags: Int,
+ animationController: ActivityTransitionAnimator.Controller?,
+ customMessage: String?,
+ disallowEnterPictureInPictureWhileLaunching: Boolean,
+ userHandle: UserHandle?,
+ ) {
+ val userHandle: UserHandle = userHandle ?: getActivityUserHandle(intent)
+
+ if (onlyProvisioned && !deviceProvisionedController.isDeviceProvisioned) return
+
+ val willLaunchResolverActivity: Boolean =
+ activityIntentHelper.wouldLaunchResolverActivity(
+ intent,
+ lockScreenUserManager.currentUserId
+ )
+
+ val animate =
+ animationController != null &&
+ !willLaunchResolverActivity &&
+ shouldAnimateLaunch(isActivityIntent = true)
+ val animController =
+ wrapAnimationControllerForShadeOrStatusBar(
+ animationController = animationController,
+ dismissShade = dismissShade,
+ isLaunchForActivity = true,
+ )
+
+ // If we animate, we will dismiss the shade only once the animation is done. This is
+ // taken care of by the StatusBarLaunchAnimationController.
+ val dismissShadeDirectly = dismissShade && animController == null
+
+ val runnable = Runnable {
+ assistManagerLazy.get().hideAssist()
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
+ intent.addFlags(flags)
+ val result = intArrayOf(ActivityManager.START_CANCELED)
+ activityTransitionAnimator.startIntentWithAnimation(
+ animController,
+ animate,
+ intent.getPackage()
+ ) { adapter: RemoteAnimationAdapter? ->
+ val options =
+ ActivityOptions(CentralSurfaces.getActivityOptions(displayId, adapter))
+
+ // We know that the intent of the caller is to dismiss the keyguard and
+ // this runnable is called right after the keyguard is solved, so we tell
+ // WM that we should dismiss it to avoid flickers when opening an activity
+ // that can also be shown over the keyguard.
+ options.setDismissKeyguardIfInsecure()
+ options.setDisallowEnterPictureInPictureWhileLaunching(
+ disallowEnterPictureInPictureWhileLaunching
+ )
+ if (CameraIntents.isInsecureCameraIntent(intent)) {
+ // Normally an activity will set it's requested rotation
+ // animation on its window. However when launching an activity
+ // causes the orientation to change this is too late. In these cases
+ // the default animation is used. This doesn't look good for
+ // the camera (as it rotates the camera contents out of sync
+ // with physical reality). So, we ask the WindowManager to
+ // force the cross fade animation if an orientation change
+ // happens to occur during the launch.
+ options.rotationAnimationHint =
+ WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
+ }
+ if (Settings.Panel.ACTION_VOLUME == intent.action) {
+ // Settings Panel is implemented as activity(not a dialog), so
+ // underlying app is paused and may enter picture-in-picture mode
+ // as a result.
+ // So we need to disable picture-in-picture mode here
+ // if it is volume panel.
+ options.setDisallowEnterPictureInPictureWhileLaunching(true)
+ }
+ try {
+ result[0] =
+ ActivityTaskManager.getService()
+ .startActivityAsUser(
+ null,
+ context.basePackageName,
+ context.attributionTag,
+ intent,
+ intent.resolveTypeIfNeeded(context.contentResolver),
+ null,
+ null,
+ 0,
+ Intent.FLAG_ACTIVITY_NEW_TASK,
+ null,
+ options.toBundle(),
+ userHandle.identifier,
+ )
+ } catch (e: RemoteException) {
+ Log.w(TAG, "Unable to start activity", e)
+ }
+ result[0]
+ }
+ callback?.onActivityStarted(result[0])
+ }
+ val cancelRunnable = Runnable {
+ callback?.onActivityStarted(ActivityManager.START_CANCELED)
+ }
+ // Do not deferKeyguard when occluded because, when keyguard is occluded,
+ // we do not launch the activity until keyguard is done.
+ val occluded = (keyguardStateController.isShowing && keyguardStateController.isOccluded)
+ val deferred = !occluded
+ executeRunnableDismissingKeyguard(
+ runnable,
+ cancelRunnable,
+ dismissShadeDirectly,
+ willLaunchResolverActivity,
+ deferred,
+ animate,
+ customMessage,
+ )
+ }
+
+ override fun startPendingIntentDismissingKeyguard(
+ intent: PendingIntent,
+ intentSentUiThreadCallback: Runnable?,
+ associatedView: View?,
+ animationController: ActivityTransitionAnimator.Controller?,
+ showOverLockscreen: Boolean,
+ fillInIntent: Intent?,
+ extraOptions: Bundle?,
+ ) {
+ val animationController =
+ if (associatedView is ExpandableNotificationRow) {
+ centralSurfaces?.getAnimatorControllerFromNotification(associatedView)
+ } else animationController
+
+ val willLaunchResolverActivity =
+ (intent.isActivity &&
+ activityIntentHelper.wouldPendingLaunchResolverActivity(
+ intent,
+ lockScreenUserManager.currentUserId,
+ ))
+
+ val actuallyShowOverLockscreen =
+ showOverLockscreen &&
+ intent.isActivity &&
+ activityIntentHelper.wouldPendingShowOverLockscreen(
+ intent,
+ lockScreenUserManager.currentUserId
+ )
+
+ val animate =
+ !willLaunchResolverActivity &&
+ animationController != null &&
+ shouldAnimateLaunch(intent.isActivity, actuallyShowOverLockscreen)
+
+ // We wrap animationCallback with a StatusBarLaunchAnimatorController so
+ // that the shade is collapsed after the animation (or when it is cancelled,
+ // aborted, etc).
+ val statusBarController =
+ wrapAnimationControllerForShadeOrStatusBar(
+ animationController = animationController,
+ dismissShade = true,
+ isLaunchForActivity = intent.isActivity,
+ )
+ val controller =
+ if (actuallyShowOverLockscreen) {
+ wrapAnimationControllerForLockscreen(statusBarController)
+ } else {
+ statusBarController
+ }
+
+ // If we animate, don't collapse the shade and defer the keyguard dismiss (in case we
+ // run the animation on the keyguard). The animation will take care of (instantly)
+ // collapsing the shade and hiding the keyguard once it is done.
+ val collapse = !animate
+ val runnable = Runnable {
+ try {
+ activityTransitionAnimator.startPendingIntentWithAnimation(
+ controller,
+ animate,
+ intent.creatorPackage,
+ actuallyShowOverLockscreen,
+ object : ActivityTransitionAnimator.PendingIntentStarter {
+ override fun startPendingIntent(
+ animationAdapter: RemoteAnimationAdapter?
+ ): Int {
+ val options =
+ ActivityOptions(
+ CentralSurfaces.getActivityOptions(displayId, animationAdapter)
+ .apply { extraOptions?.let { putAll(it) } }
+ )
+ // TODO b/221255671: restrict this to only be set for
+ // notifications
+ options.isEligibleForLegacyPermissionPrompt = true
+ options.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ )
+ return intent.sendAndReturnResult(
+ context,
+ 0,
+ fillInIntent,
+ null,
+ null,
+ null,
+ options.toBundle()
+ )
+ }
+ },
+ )
+ } catch (e: PendingIntent.CanceledException) {
+ // the stack trace isn't very helpful here.
+ // Just log the exception message.
+ Log.w(TAG, "Sending intent failed: $e")
+ if (!collapse) {
+ // executeRunnableDismissingKeyguard did not collapse for us already.
+ shadeControllerLazy.get().collapseOnMainThread()
+ }
+ // TODO: Dismiss Keyguard.
+ }
+ if (intent.isActivity) {
+ assistManagerLazy.get().hideAssist()
+ // This activity could have started while the device is dreaming, in which case
+ // the dream would occlude the activity. In order to show the newly started
+ // activity, we wake from the dream.
+ keyguardUpdateMonitor.awakenFromDream()
+ }
+ intentSentUiThreadCallback?.let { postOnUiThread(runnable = it) }
+ }
+
+ if (!actuallyShowOverLockscreen) {
+ postOnUiThread(delay = 0) {
+ executeRunnableDismissingKeyguard(
+ runnable = runnable,
+ afterKeyguardGone = willLaunchResolverActivity,
+ dismissShade = collapse,
+ willAnimateOnKeyguard = animate,
+ )
+ }
+ } else {
+ postOnUiThread(delay = 0, runnable)
+ }
+ }
+
+ override fun startActivity(
+ intent: Intent,
+ dismissShade: Boolean,
+ animationController: ActivityTransitionAnimator.Controller?,
+ showOverLockscreenWhenLocked: Boolean,
+ userHandle: UserHandle?,
+ ) {
+ val userHandle = userHandle ?: getActivityUserHandle(intent)
+ // Make sure that we dismiss the keyguard if it is directly dismissible or when we don't
+ // want to show the activity above it.
+ if (keyguardStateController.isUnlocked || !showOverLockscreenWhenLocked) {
+ startActivityDismissingKeyguard(
+ intent = intent,
+ onlyProvisioned = false,
+ dismissShade = dismissShade,
+ disallowEnterPictureInPictureWhileLaunching = false,
+ callback = null,
+ flags = 0,
+ animationController = animationController,
+ userHandle = userHandle,
+ )
+ return
+ }
+
+ val animate =
+ animationController != null &&
+ shouldAnimateLaunch(/* isActivityIntent= */ true, showOverLockscreenWhenLocked)
+
+ var controller: ActivityTransitionAnimator.Controller? = null
+ if (animate) {
+ // Wrap the animation controller to dismiss the shade and set
+ // mIsLaunchingActivityOverLockscreen during the animation.
+ val delegate =
+ wrapAnimationControllerForShadeOrStatusBar(
+ animationController = animationController,
+ dismissShade = dismissShade,
+ isLaunchForActivity = true,
+ )
+ controller = wrapAnimationControllerForLockscreen(delegate)
+ } else if (dismissShade) {
+ // The animation will take care of dismissing the shade at the end of the animation.
+ // If we don't animate, collapse it directly.
+ shadeControllerLazy.get().cancelExpansionAndCollapseShade()
+ }
+
+ // We should exit the dream to prevent the activity from starting below the
+ // dream.
+ if (keyguardUpdateMonitor.isDreaming) {
+ centralSurfaces?.awakenDreams()
+ }
+
+ activityTransitionAnimator.startIntentWithAnimation(
+ controller,
+ animate,
+ intent.getPackage(),
+ showOverLockscreenWhenLocked
+ ) { adapter: RemoteAnimationAdapter? ->
+ TaskStackBuilder.create(context)
+ .addNextIntent(intent)
+ .startActivities(CentralSurfaces.getActivityOptions(displayId, adapter), userHandle)
+ }
+ }
+
+ override fun dismissKeyguardThenExecute(
+ action: ActivityStarter.OnDismissAction,
+ cancel: Runnable?,
+ afterKeyguardGone: Boolean,
+ customMessage: String?,
+ ) {
+ if (
+ !action.willRunAnimationOnKeyguard() &&
+ wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_ASLEEP &&
+ keyguardStateController.canDismissLockScreen() &&
+ !statusBarStateController.leaveOpenOnKeyguardHide() &&
+ dozeServiceHostLazy.get().isPulsing
+ ) {
+ // Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a
+ // pulse.
+ // TODO: Factor this transition out of BiometricUnlockController.
+ biometricUnlockControllerLazy
+ .get()
+ .startWakeAndUnlock(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
+ }
+ if (keyguardStateController.isShowing) {
+ statusBarKeyguardViewManagerLazy
+ .get()
+ .dismissWithAction(action, cancel, afterKeyguardGone, customMessage)
+ } else {
+ // If the keyguard isn't showing but the device is dreaming, we should exit the
+ // dream.
+ if (keyguardUpdateMonitor.isDreaming) {
+ centralSurfaces?.awakenDreams()
+ }
+ action.onDismiss()
+ }
+ }
+
+ override fun executeRunnableDismissingKeyguard(
+ runnable: Runnable?,
+ cancelAction: Runnable?,
+ dismissShade: Boolean,
+ afterKeyguardGone: Boolean,
+ deferred: Boolean,
+ willAnimateOnKeyguard: Boolean,
+ customMessage: String?,
+ ) {
+ val onDismissAction: ActivityStarter.OnDismissAction =
+ object : ActivityStarter.OnDismissAction {
+ override fun onDismiss(): Boolean {
+ if (runnable != null) {
+ if (
+ keyguardStateController.isShowing && keyguardStateController.isOccluded
+ ) {
+ statusBarKeyguardViewManagerLazy
+ .get()
+ .addAfterKeyguardGoneRunnable(runnable)
+ } else {
+ mainExecutor.execute(runnable)
+ }
+ }
+ if (dismissShade) {
+ shadeControllerLazy.get().collapseShadeForActivityStart()
+ }
+ return deferred
+ }
+
+ override fun willRunAnimationOnKeyguard(): Boolean {
+ return willAnimateOnKeyguard
+ }
+ }
+ dismissKeyguardThenExecute(
+ onDismissAction,
+ cancelAction,
+ afterKeyguardGone,
+ customMessage,
+ )
+ }
+
+ /**
+ * Return a [ActivityTransitionAnimator.Controller] wrapping `animationController` so that:
+ * - if it launches in the notification shade window and `dismissShade` is true, then the shade
+ * will be instantly dismissed at the end of the animation.
+ * - if it launches in status bar window, it will make the status bar window match the device
+ * size during the animation (that way, the animation won't be clipped by the status bar
+ * size).
+ *
+ * @param animationController the controller that is wrapped and will drive the main animation.
+ * @param dismissShade whether the notification shade will be dismissed at the end of the
+ * animation. This is ignored if `animationController` is not animating in the shade window.
+ * @param isLaunchForActivity whether the launch is for an activity.
+ */
+ private fun wrapAnimationControllerForShadeOrStatusBar(
+ animationController: ActivityTransitionAnimator.Controller?,
+ dismissShade: Boolean,
+ isLaunchForActivity: Boolean,
+ ): ActivityTransitionAnimator.Controller? {
+ if (animationController == null) {
+ return null
+ }
+ val rootView = animationController.transitionContainer.rootView
+ val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> =
+ statusBarWindowController.wrapAnimationControllerIfInStatusBar(
+ rootView,
+ animationController
+ )
+ if (controllerFromStatusBar.isPresent) {
+ return controllerFromStatusBar.get()
+ }
+
+ centralSurfaces?.let {
+ // If the view is not in the status bar, then we are animating a view in the shade.
+ // We have to make sure that we collapse it when the animation ends or is cancelled.
+ if (dismissShade) {
+ return StatusBarTransitionAnimatorController(
+ animationController,
+ shadeAnimationInteractor,
+ shadeControllerLazy.get(),
+ notifShadeWindowControllerLazy.get(),
+ commandQueue,
+ displayId,
+ isLaunchForActivity
+ )
+ }
+ }
+
+ return animationController
+ }
+
+ /**
+ * Wraps an animation controller so that if an activity would be launched on top of the
+ * lockscreen, the correct flags are set for it to be occluded.
+ */
+ private fun wrapAnimationControllerForLockscreen(
+ animationController: ActivityTransitionAnimator.Controller?
+ ): ActivityTransitionAnimator.Controller? {
+ return animationController?.let {
+ object : DelegateTransitionAnimatorController(it) {
+ override fun onIntentStarted(willAnimate: Boolean) {
+ delegate.onIntentStarted(willAnimate)
+ if (willAnimate) {
+ centralSurfaces?.setIsLaunchingActivityOverLockscreen(true)
+ }
+ }
+
+ override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
+ super.onTransitionAnimationStart(isExpandingFullyAbove)
+
+ // Double check that the keyguard is still showing and not going
+ // away, but if so set the keyguard occluded. Typically, WM will let
+ // KeyguardViewMediator know directly, but we're overriding that to
+ // play the custom launch animation, so we need to take care of that
+ // here. The unocclude animation is not overridden, so WM will call
+ // KeyguardViewMediator's unocclude animation runner when the
+ // activity is exited.
+ if (
+ keyguardStateController.isShowing &&
+ !keyguardStateController.isKeyguardGoingAway
+ ) {
+ Log.d(TAG, "Setting occluded = true in #startActivity.")
+ keyguardViewMediatorLazy
+ .get()
+ .setOccluded(true /* isOccluded */, true /* animate */)
+ }
+ }
+
+ override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
+ // Set mIsLaunchingActivityOverLockscreen to false before actually
+ // finishing the animation so that we can assume that
+ // mIsLaunchingActivityOverLockscreen being true means that we will
+ // collapse the shade (or at least run the post collapse runnables)
+ // later on.
+ centralSurfaces?.setIsLaunchingActivityOverLockscreen(false)
+ delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
+ }
+
+ override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+ if (newKeyguardOccludedState != null) {
+ keyguardViewMediatorLazy
+ .get()
+ .setOccluded(newKeyguardOccludedState, false /* animate */)
+ }
+
+ // Set mIsLaunchingActivityOverLockscreen to false before actually
+ // finishing the animation so that we can assume that
+ // mIsLaunchingActivityOverLockscreen being true means that we will
+ // collapse the shade (or at least run the // post collapse
+ // runnables) later on.
+ centralSurfaces?.setIsLaunchingActivityOverLockscreen(false)
+ delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
+ }
+ }
+ }
+ }
+
+ /** Retrieves the current user handle to start the Activity. */
+ private fun getActivityUserHandle(intent: Intent): UserHandle {
+ val packages: Array<String> = context.resources.getStringArray(R.array.system_ui_packages)
+ for (pkg in packages) {
+ val componentName = intent.component ?: break
+ if (pkg == componentName.packageName) {
+ return UserHandle(UserHandle.myUserId())
+ }
+ }
+ return userTracker.userHandle
+ }
+
+ /**
+ * Whether we should animate an activity launch.
+ *
+ * Note: This method must be called *before* dismissing the keyguard.
+ */
+ private fun shouldAnimateLaunch(
+ isActivityIntent: Boolean,
+ showOverLockscreen: Boolean,
+ ): Boolean {
+ // TODO(b/294418322): Support launch animations when occluded.
+ if (keyguardStateController.isOccluded) {
+ return false
+ }
+
+ // Always animate if we are not showing the keyguard or if we animate over the lockscreen
+ // (without unlocking it).
+ if (showOverLockscreen || !keyguardStateController.isShowing) {
+ return true
+ }
+
+ // We don't animate non-activity launches as they can break the animation.
+ // TODO(b/184121838): Support non activity launches on the lockscreen.
+ return isActivityIntent
+ }
+
+ override fun shouldAnimateLaunch(isActivityIntent: Boolean): Boolean {
+ return shouldAnimateLaunch(isActivityIntent, false)
+ }
+
+ private fun postOnUiThread(delay: Int = 0, runnable: Runnable) {
+ mainExecutor.executeDelayed(runnable, delay.toLong())
+ }
+
+ companion object {
+ private const val TAG = "LegacyActivityStarterInternalImpl"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 92fd90a..5206e46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -24,10 +24,10 @@
import android.view.ViewGroup
import android.view.ViewTreeObserver
import com.android.systemui.Gefingerpoken
-import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.ShadeLogger
@@ -65,7 +65,6 @@
private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
private val userChipViewModel: StatusBarUserChipViewModel,
private val viewUtil: ViewUtil,
- private val sceneContainerFlags: SceneContainerFlags,
private val configurationController: ConfigurationController,
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
) : ViewController<PhoneStatusBarView>(view) {
@@ -205,7 +204,7 @@
// If scene framework is enabled, route the touch to it and
// ignore the rest of the gesture.
- if (sceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
windowRootView.get().dispatchTouchEvent(event)
return true
}
@@ -267,7 +266,6 @@
@Named(UNFOLD_STATUS_BAR)
private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
private val featureFlags: FeatureFlags,
- private val sceneContainerFlags: SceneContainerFlags,
private val userChipViewModel: StatusBarUserChipViewModel,
private val centralSurfaces: CentralSurfaces,
private val statusBarWindowStateController: StatusBarWindowStateController,
@@ -301,7 +299,6 @@
statusBarMoveFromCenterAnimationController,
userChipViewModel,
viewUtil,
- sceneContainerFlags,
configurationController,
statusOverlayHoverListenerFactory,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index ed1f6ff..da5877b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -98,15 +99,21 @@
// we need to keep the panel open artificially, let's wait until the
//animation
// is finished.
- mHeadsUpManager.setHeadsUpGoingAway(true);
+ setHeadsAnimatingAway(true);
mNsslController.runAfterAnimationFinished(() -> {
if (!mHeadsUpManager.hasPinnedHeadsUp()) {
mNotificationShadeWindowController.setHeadsUpShowing(false);
- mHeadsUpManager.setHeadsUpGoingAway(false);
+ setHeadsAnimatingAway(false);
}
mNotificationRemoteInputManager.onPanelCollapsed();
});
}
}
}
+
+ private void setHeadsAnimatingAway(boolean headsUpAnimatingAway) {
+ if (!NotificationsHeadsUpRefactor.isEnabled()) {
+ mHeadsUpManager.setHeadsUpAnimatingAway(headsUpAnimatingAway);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 5f26702..a141b53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -67,12 +67,12 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardWmStateRefactor;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
+import com.android.systemui.keyguard.shared.RefactorKeyguardDismissIntent;
import com.android.systemui.keyguard.shared.model.DismissAction;
import com.android.systemui.keyguard.shared.model.KeyguardDone;
import com.android.systemui.keyguard.shared.model.KeyguardState;
@@ -332,7 +332,6 @@
private final LatencyTracker mLatencyTracker;
private final KeyguardSecurityModel mKeyguardSecurityModel;
private final SelectedUserInteractor mSelectedUserInteractor;
- @Nullable private KeyguardBypassController mBypassController;
@Nullable private OccludingAppBiometricUI mOccludingAppBiometricUI;
@Nullable private TaskbarDelegate mTaskbarDelegate;
@@ -381,7 +380,6 @@
Lazy<ShadeController> shadeController,
LatencyTracker latencyTracker,
KeyguardSecurityModel keyguardSecurityModel,
- FeatureFlags featureFlags,
PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor,
PrimaryBouncerInteractor primaryBouncerInteractor,
BouncerView primaryBouncerView,
@@ -413,7 +411,6 @@
mShadeController = shadeController;
mLatencyTracker = latencyTracker;
mKeyguardSecurityModel = keyguardSecurityModel;
- mFlags = featureFlags;
mPrimaryBouncerCallbackInteractor = primaryBouncerCallbackInteractor;
mPrimaryBouncerInteractor = primaryBouncerInteractor;
mPrimaryBouncerView = primaryBouncerView;
@@ -442,8 +439,7 @@
ShadeLockscreenInteractor shadeLockscreenInteractor,
ShadeExpansionStateManager shadeExpansionStateManager,
BiometricUnlockController biometricUnlockController,
- View notificationContainer,
- KeyguardBypassController bypassController) {
+ View notificationContainer) {
mCentralSurfaces = centralSurfaces;
mBiometricUnlockController = biometricUnlockController;
@@ -454,7 +450,6 @@
shadeExpansionStateManager.addExpansionListener(this);
onPanelExpansionChanged(currentState);
}
- mBypassController = bypassController;
mNotificationContainer = notificationContainer;
if (!DeviceEntryUdfpsRefactor.isEnabled()) {
mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create(
@@ -805,7 +800,7 @@
public void dismissWithAction(OnDismissAction r, Runnable cancelAction,
boolean afterKeyguardGone, String message) {
- if (mFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ if (RefactorKeyguardDismissIntent.isEnabled()) {
if (r == null) {
return;
}
@@ -857,7 +852,7 @@
return;
}
- if (!mFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ if (!RefactorKeyguardDismissIntent.isEnabled()) {
mAfterKeyguardGoneAction = r;
mKeyguardGoneCancelAction = cancelAction;
mDismissActionWillAnimateOnKeyguard = r != null
@@ -925,7 +920,7 @@
* Adds a {@param runnable} to be executed after Keyguard is gone.
*/
public void addAfterKeyguardGoneRunnable(Runnable runnable) {
- if (mFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ if (RefactorKeyguardDismissIntent.isEnabled()) {
if (runnable != null) {
mKeyguardDismissActionInteractor.get().runAfterKeyguardGone(runnable);
}
@@ -975,7 +970,6 @@
mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer);
mKeyguardMessageAreaController.setMessage("");
}
- mBypassController.setAltBouncerShowing(isShowingAlternateBouncer);
mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer);
if (updateScrim) {
@@ -1118,7 +1112,7 @@
// We update the state (which will show the keyguard) only if an animation will run on
// the keyguard. If there is no animation, we wait before updating the state so that we
// go directly from bouncer to launcher/app.
- if (mFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ if (RefactorKeyguardDismissIntent.isEnabled()) {
if (mKeyguardDismissActionInteractor.get().runDismissAnimationOnKeyguard()) {
updateStates();
}
@@ -1245,7 +1239,7 @@
}
private void executeAfterKeyguardGoneAction() {
- if (mFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) {
+ if (RefactorKeyguardDismissIntent.isEnabled()) {
return;
}
if (mAfterKeyguardGoneAction != null) {
@@ -1298,9 +1292,11 @@
return;
}
+ boolean hideBouncerOverDream = isBouncerShowing()
+ && mDreamOverlayStateController.isOverlayActive();
mCentralSurfaces.endAffordanceLaunch();
// The second condition is for SIM card locked bouncer
- if (primaryBouncerIsScrimmed() && !needsFullscreenBouncer()) {
+ if (hideBouncerOverDream || (primaryBouncerIsScrimmed() && !needsFullscreenBouncer())) {
hideBouncer(false);
updateStates();
} else {
@@ -1634,7 +1630,7 @@
pw.println(" bouncerIsOrWillBeShowing(): " + primaryBouncerIsOrWillBeShowing());
pw.println(" Registered KeyguardViewManagerCallbacks:");
pw.println(" refactorKeyguardDismissIntent enabled:"
- + mFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT));
+ + RefactorKeyguardDismissIntent.isEnabled());
for (KeyguardViewManagerCallback callback : mCallbacks) {
pw.println(" " + callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index c615887..d1189e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -37,7 +37,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -91,7 +91,6 @@
ShadeInteractor shadeInteractor,
Provider<SceneInteractor> sceneInteractor,
JavaAdapter javaAdapter,
- SceneContainerFlags sceneContainerFlags,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
PrimaryBouncerInteractor primaryBouncerInteractor,
AlternateBouncerInteractor alternateBouncerInteractor
@@ -121,7 +120,7 @@
updateTouchableRegion();
}
});
- mHeadsUpManager.addHeadsUpPhoneListener(this::onHeadsUpGoingAwayStateChanged);
+ mHeadsUpManager.addHeadsUpPhoneListener(this::onHeadsUpAnimatingAwayStateChanged);
mNotificationShadeWindowController = notificationShadeWindowController;
mNotificationShadeWindowController.setForcePluginOpenListener((forceOpen) -> {
@@ -130,7 +129,7 @@
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
- if (sceneContainerFlags.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
javaAdapter.alwaysCollectFlow(
sceneInteractor.get().isVisible(),
this::onSceneContainerVisibilityChanged);
@@ -214,7 +213,7 @@
&& (mNotificationShadeWindowView.getRootWindowInsets() != null)
&& (mNotificationShadeWindowView.getRootWindowInsets().getDisplayCutout() != null);
boolean shouldObserve = mHeadsUpManager.hasPinnedHeadsUp()
- || mHeadsUpManager.isHeadsUpGoingAway()
+ || mHeadsUpManager.isHeadsUpAnimatingAwayValue()
|| mForceCollapsedUntilLayout
|| hasCutoutInset
|| mNotificationShadeWindowController.getForcePluginOpen();
@@ -288,8 +287,8 @@
|| mUnlockedScreenOffAnimationController.isAnimationPlaying();
}
- private void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway) {
- if (!headsUpGoingAway) {
+ private void onHeadsUpAnimatingAwayStateChanged(boolean headsUpAnimatingAway) {
+ if (!headsUpAnimatingAway) {
updateTouchableRegionAfterLayout();
} else {
updateTouchableRegion();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt
index 541ac48..31c2134 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt
@@ -15,36 +15,55 @@
*/
package com.android.systemui.statusbar.phone
+import android.annotation.StyleRes
import android.app.Dialog
import android.content.Context
-import android.content.res.Configuration
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.Gravity
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.view.WindowInsets
-import android.view.WindowInsets.Type.InsetsType
-import android.view.WindowInsetsAnimation
import android.view.WindowManager.LayoutParams.MATCH_PARENT
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL
+import androidx.activity.ComponentDialog
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import com.android.systemui.statusbar.policy.onConfigChanged
+import dagger.Lazy
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
/** A dialog shown as a bottom sheet. */
-open class SystemUIBottomSheetDialog(
+class SystemUIBottomSheetDialog
+@VisibleForTesting
+constructor(
context: Context,
- private val configurationController: ConfigurationController? = null,
- theme: Int = R.style.Theme_SystemUI_Dialog
-) : Dialog(context, theme) {
+ private val coroutineScope: CoroutineScope,
+ private val configurationController: ConfigurationController,
+ private val delegate: DialogDelegate<in Dialog>,
+ private val windowLayout: WindowLayout,
+ theme: Int,
+) : ComponentDialog(context, theme) {
+
+ private var job: Job? = null
+
override fun onCreate(savedInstanceState: Bundle?) {
+ delegate.beforeCreate(this, savedInstanceState)
super.onCreate(savedInstanceState)
setupWindow()
- setupEdgeToEdge()
setCanceledOnTouchOutside(true)
+ delegate.onCreate(this, savedInstanceState)
}
private fun setupWindow() {
@@ -62,60 +81,84 @@
}
}
- private fun setupEdgeToEdge() {
- val edgeToEdgeHorizontally =
- context.resources.getBoolean(R.bool.config_edgeToEdgeBottomSheetDialog)
- val width = if (edgeToEdgeHorizontally) MATCH_PARENT else WRAP_CONTENT
- val height = WRAP_CONTENT
- window?.setLayout(width, height)
- }
-
override fun onStart() {
super.onStart()
- configurationController?.addCallback(onConfigChanged)
- window?.decorView?.setWindowInsetsAnimationCallback(insetsAnimationCallback)
+ job?.cancel()
+ job =
+ coroutineScope.launch {
+ windowLayout
+ .calculate()
+ .onEach { window?.apply { setLayout(it.width, it.height) } }
+ .launchIn(this)
+ configurationController.onConfigChanged
+ .onEach { delegate.onConfigurationChanged(this@SystemUIBottomSheetDialog, it) }
+ .launchIn(this)
+ }
+ delegate.onStart(this)
}
override fun onStop() {
+ job?.cancel()
+ delegate.onStop(this)
super.onStop()
- configurationController?.removeCallback(onConfigChanged)
- window?.decorView?.setWindowInsetsAnimationCallback(null)
}
- /** Called after any insets change. */
- open fun onInsetsChanged(@InsetsType changedTypes: Int, insets: WindowInsets) {}
+ override fun onWindowFocusChanged(hasFocus: Boolean) {
+ super.onWindowFocusChanged(hasFocus)
+ delegate.onWindowFocusChanged(this, hasFocus)
+ }
- /** Can be overridden by subclasses to receive config changed events. */
- open fun onConfigurationChanged() {}
+ class Factory
+ @Inject
+ constructor(
+ @Application private val context: Context,
+ @Application private val coroutineScope: CoroutineScope,
+ private val defaultWindowLayout: Lazy<WindowLayout.LimitedEdgeToEdge>,
+ private val configurationController: ConfigurationController,
+ ) {
- private val insetsAnimationCallback =
- object : WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+ fun create(
+ delegate: DialogDelegate<in Dialog>,
+ windowLayout: WindowLayout = defaultWindowLayout.get(),
+ @StyleRes theme: Int = R.style.Theme_SystemUI_Dialog,
+ ): SystemUIBottomSheetDialog =
+ SystemUIBottomSheetDialog(
+ context = context,
+ configurationController = configurationController,
+ coroutineScope = coroutineScope,
+ delegate = delegate,
+ windowLayout = windowLayout,
+ theme = theme,
+ )
+ }
- private var lastInsets: WindowInsets? = null
+ /** [SystemUIBottomSheetDialog] uses this to determine the [android.view.Window] layout. */
+ interface WindowLayout {
- override fun onEnd(animation: WindowInsetsAnimation) {
- lastInsets?.let { onInsetsChanged(animation.typeMask, it) }
- }
+ /** Returns a [Layout] to apply to [android.view.Window.setLayout]. */
+ fun calculate(): Flow<Layout>
- override fun onProgress(
- insets: WindowInsets,
- animations: MutableList<WindowInsetsAnimation>,
- ): WindowInsets {
- lastInsets = insets
- onInsetsChanged(changedTypes = allAnimationMasks(animations), insets)
- return insets
- }
+ /** Edge to edge with which doesn't fill the whole space on the large screen. */
+ class LimitedEdgeToEdge
+ @Inject
+ constructor(
+ @Application private val context: Context,
+ private val configurationController: ConfigurationController,
+ ) : WindowLayout {
- private fun allAnimationMasks(animations: List<WindowInsetsAnimation>): Int =
- animations.fold(0) { acc: Int, it -> acc or it.typeMask }
- }
+ override fun calculate(): Flow<Layout> {
+ return configurationController.onConfigChanged
+ .onStart { emit(context.resources.configuration) }
+ .map {
+ val edgeToEdgeHorizontally =
+ context.resources.getBoolean(R.bool.config_edgeToEdgeBottomSheetDialog)
+ val width = if (edgeToEdgeHorizontally) MATCH_PARENT else WRAP_CONTENT
- private val onConfigChanged =
- object : ConfigurationListener {
- override fun onConfigChanged(newConfig: Configuration?) {
- super.onConfigChanged(newConfig)
- setupEdgeToEdge()
- onConfigurationChanged()
+ Layout(width, WRAP_CONTENT)
+ }
}
}
+
+ data class Layout(val width: Int, val height: Int)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
index 3b2930f..f4e3eab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
@@ -18,7 +18,6 @@
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
-import android.telephony.CarrierConfigManager.KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT
import android.telephony.CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.flow.MutableStateFlow
@@ -43,11 +42,10 @@
* using the default config for logging purposes.
*
* NOTE to add new keys to be tracked:
- * 1. Define a new `private val` wrapping the key using [BooleanCarrierConfig] or [IntCarrierConfig]
- * 2. Define a public `val` exposing the wrapped flow using [BooleanCarrierConfig.config] or
- * [IntCarrierConfig.config]
- * 3. Add the new wrapped public flow to the list of tracked configs, so they are properly updated
- * when a new carrier config comes down
+ * 1. Define a new `private val` wrapping the key using [BooleanCarrierConfig]
+ * 2. Define a public `val` exposing the wrapped flow using [BooleanCarrierConfig.config]
+ * 3. Add the new [BooleanCarrierConfig] to the list of tracked configs, so they are properly
+ * updated when a new carrier config comes down
*/
class SystemUiCarrierConfig
internal constructor(
@@ -68,16 +66,10 @@
/** Flow tracking the [KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL] config */
val showOperatorNameInStatusBar: StateFlow<Boolean> = showOperatorName.config
- private val satelliteHysteresisSeconds =
- IntCarrierConfig(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, defaultConfig)
- /** Flow tracking the [KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT] config */
- val satelliteConnectionHysteresisSeconds: StateFlow<Int> = satelliteHysteresisSeconds.config
-
private val trackedConfigs =
listOf(
inflateSignalStrength,
showOperatorName,
- satelliteHysteresisSeconds,
)
/** Ingest a new carrier config, and switch all of the tracked keys over to the new values */
@@ -98,19 +90,15 @@
override fun toString(): String = trackedConfigs.joinToString { it.toString() }
}
-interface CarrierConfig {
- fun update(config: PersistableBundle)
-}
-
/** Extracts [key] from the carrier config, and stores it in a flow */
private class BooleanCarrierConfig(
val key: String,
defaultConfig: PersistableBundle,
-) : CarrierConfig {
+) {
private val _configValue = MutableStateFlow(defaultConfig.getBoolean(key))
val config = _configValue.asStateFlow()
- override fun update(config: PersistableBundle) {
+ fun update(config: PersistableBundle) {
_configValue.value = config.getBoolean(key)
}
@@ -118,20 +106,3 @@
return "$key=${config.value}"
}
}
-
-/** Extracts [key] from the carrier config, and stores it in a flow */
-private class IntCarrierConfig(
- val key: String,
- defaultConfig: PersistableBundle,
-) : CarrierConfig {
- private val _configValue = MutableStateFlow(defaultConfig.getInt(key))
- val config = _configValue.asStateFlow()
-
- override fun update(config: PersistableBundle) {
- _configValue.value = config.getInt(key)
- }
-
- override fun toString(): String {
- return "$key=${config.value}"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index 8f00b43..2278597 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -44,6 +44,9 @@
/** The carrierId for this connection. See [TelephonyManager.getSimCarrierId] */
val carrierId: StateFlow<Int>
+ /** Reflects the value from the carrier config INFLATE_SIGNAL_STRENGTH for this connection */
+ val inflateSignalStrength: StateFlow<Boolean>
+
/**
* The table log buffer created for this connection. Will have the name "MobileConnectionLog
* [subId]"
@@ -141,9 +144,6 @@
*/
val hasPrioritizedNetworkCapabilities: StateFlow<Boolean>
- /** Duration in seconds of the hysteresis to use when losing satellite connection. */
- val satelliteConnectionHysteresisSeconds: StateFlow<Int>
-
/**
* True if this connection is in emergency callback mode.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
index af34a57..83d5f2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
@@ -43,6 +43,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/**
@@ -67,6 +68,17 @@
)
.stateIn(scope, SharingStarted.WhileSubscribed(), _carrierId.value)
+ private val _inflateSignalStrength: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ override val inflateSignalStrength =
+ _inflateSignalStrength
+ .logDiffsForTable(
+ tableLogBuffer,
+ columnPrefix = "",
+ columnName = "inflate",
+ _inflateSignalStrength.value
+ )
+ .stateIn(scope, SharingStarted.WhileSubscribed(), _inflateSignalStrength.value)
+
private val _isEmergencyOnly = MutableStateFlow(false)
override val isEmergencyOnly =
_isEmergencyOnly
@@ -191,7 +203,16 @@
.logDiffsForTable(tableLogBuffer, columnPrefix = "", _resolvedNetworkType.value)
.stateIn(scope, SharingStarted.WhileSubscribed(), _resolvedNetworkType.value)
- override val numberOfLevels = MutableStateFlow(MobileConnectionRepository.DEFAULT_NUM_LEVELS)
+ override val numberOfLevels =
+ _inflateSignalStrength
+ .map { shouldInflate ->
+ if (shouldInflate) {
+ DEFAULT_NUM_LEVELS + 1
+ } else {
+ DEFAULT_NUM_LEVELS
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
override val dataEnabled = MutableStateFlow(true)
@@ -206,8 +227,6 @@
override val hasPrioritizedNetworkCapabilities = MutableStateFlow(false)
- override val satelliteConnectionHysteresisSeconds = MutableStateFlow(0)
-
override suspend fun isInEcmMode(): Boolean = false
/**
@@ -226,8 +245,7 @@
_carrierId.value = event.carrierId ?: INVALID_SUBSCRIPTION_ID
- numberOfLevels.value =
- if (event.inflateStrength) DEFAULT_NUM_LEVELS + 1 else DEFAULT_NUM_LEVELS
+ _inflateSignalStrength.value = event.inflateStrength
cdmaRoaming.value = event.roaming
_isRoaming.value = event.roaming
@@ -258,7 +276,6 @@
carrierName.value = NetworkNameModel.SubscriptionDerived(CARRIER_MERGED_NAME)
// TODO(b/276943904): is carrierId a thing with carrier merged networks?
_carrierId.value = INVALID_SUBSCRIPTION_ID
- numberOfLevels.value = event.numberOfLevels
cdmaRoaming.value = false
_primaryLevel.value = event.level
_cdmaLevel.value = event.level
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
index 2bc3bcb..a532e62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -165,6 +165,7 @@
override val isRoaming = MutableStateFlow(false).asStateFlow()
override val carrierId = MutableStateFlow(INVALID_SUBSCRIPTION_ID).asStateFlow()
+ override val inflateSignalStrength = MutableStateFlow(false).asStateFlow()
override val isEmergencyOnly = MutableStateFlow(false).asStateFlow()
override val operatorAlphaShort = MutableStateFlow(null).asStateFlow()
override val isInService = MutableStateFlow(true).asStateFlow()
@@ -186,9 +187,6 @@
*/
override val hasPrioritizedNetworkCapabilities = MutableStateFlow(false).asStateFlow()
- /** Non-applicable to carrier merged connections. */
- override val satelliteConnectionHysteresisSeconds = MutableStateFlow(0).asStateFlow()
-
override val dataEnabled: StateFlow<Boolean> = wifiRepository.isWifiEnabled
override suspend fun isInEcmMode(): Boolean =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index b085d80..41559b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
+import android.util.IndentingPrintWriter
import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.table.TableLogBuffer
@@ -24,6 +25,7 @@
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -291,6 +293,21 @@
)
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.dataEnabled.value)
+ override val inflateSignalStrength =
+ activeRepo
+ .flatMapLatest { it.inflateSignalStrength }
+ .logDiffsForTable(
+ tableLogBuffer,
+ columnPrefix = "",
+ columnName = "inflate",
+ initialValue = activeRepo.value.inflateSignalStrength.value,
+ )
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ activeRepo.value.inflateSignalStrength.value
+ )
+
override val numberOfLevels =
activeRepo
.flatMapLatest { it.numberOfLevels }
@@ -334,17 +351,29 @@
activeRepo.value.hasPrioritizedNetworkCapabilities.value,
)
- override val satelliteConnectionHysteresisSeconds =
- activeRepo
- .flatMapLatest { it.satelliteConnectionHysteresisSeconds }
- .stateIn(
- scope,
- SharingStarted.WhileSubscribed(),
- activeRepo.value.satelliteConnectionHysteresisSeconds.value
- )
-
override suspend fun isInEcmMode(): Boolean = activeRepo.value.isInEcmMode()
+ fun dump(pw: PrintWriter) {
+ val ipw = IndentingPrintWriter(pw, " ")
+
+ ipw.println("MobileConnectionRepository[$subId]")
+ ipw.increaseIndent()
+
+ ipw.println("carrierMerged=${_isCarrierMerged.value}")
+
+ ipw.print("Type (cellular or carrier merged): ")
+ when (activeRepo.value) {
+ is CarrierMergedConnectionRepository -> ipw.println("Carrier merged")
+ is MobileConnectionRepositoryImpl -> ipw.println("Cellular")
+ }
+
+ ipw.increaseIndent()
+ ipw.println("Provider: ${activeRepo.value}")
+ ipw.decreaseIndent()
+
+ ipw.decreaseIndent()
+ }
+
class Factory
@Inject
constructor(
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 5ab2ae8..b3885d2 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
@@ -302,8 +302,10 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), UnknownNetworkType)
+ override val inflateSignalStrength = systemUiCarrierConfig.shouldInflateSignalStrength
+
override val numberOfLevels =
- systemUiCarrierConfig.shouldInflateSignalStrength
+ inflateSignalStrength
.map { shouldInflate ->
if (shouldInflate) {
DEFAULT_NUM_LEVELS + 1
@@ -448,9 +450,6 @@
.flowOn(bgDispatcher)
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
- override val satelliteConnectionHysteresisSeconds: StateFlow<Int> =
- systemUiCarrierConfig.satelliteConnectionHysteresisSeconds
-
class Factory
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index a455db2..5d91ef3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -26,17 +26,20 @@
import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
import android.telephony.TelephonyManager
+import android.util.IndentingPrintWriter
import androidx.annotation.VisibleForTesting
import com.android.internal.telephony.PhoneConstants
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings.Config
+import com.android.systemui.Dumpable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dump.DumpManager
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.res.R
@@ -52,6 +55,8 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.util.kotlin.pairwise
+import java.io.PrintWriter
+import java.lang.ref.WeakReference
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -97,8 +102,12 @@
wifiRepository: WifiRepository,
private val fullMobileRepoFactory: FullMobileConnectionRepository.Factory,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-) : MobileConnectionsRepository {
- private var subIdRepositoryCache: MutableMap<Int, FullMobileConnectionRepository> =
+ private val dumpManager: DumpManager,
+) : MobileConnectionsRepository, Dumpable {
+
+ // TODO(b/333912012): for now, we are never invalidating the cache. We can do better though
+ private var subIdRepositoryCache:
+ MutableMap<Int, WeakReference<FullMobileConnectionRepository>> =
mutableMapOf()
private val defaultNetworkName =
@@ -109,6 +118,10 @@
private val networkNameSeparator: String =
context.getString(R.string.status_bar_network_name_separator)
+ init {
+ dumpManager.registerNormalDumpable("MobileConnectionsRepository", this)
+ }
+
private val carrierMergedSubId: StateFlow<Int?> =
combine(
wifiRepository.wifiNetwork,
@@ -283,8 +296,10 @@
getOrCreateRepoForSubId(subId)
private fun getOrCreateRepoForSubId(subId: Int) =
- subIdRepositoryCache[subId]
- ?: createRepositoryForSubId(subId).also { subIdRepositoryCache[subId] = it }
+ subIdRepositoryCache[subId]?.get()
+ ?: createRepositoryForSubId(subId).also {
+ subIdRepositoryCache[subId] = WeakReference(it)
+ }
override val mobileIsDefault: StateFlow<Boolean> =
connectivityRepository.defaultConnections
@@ -374,9 +389,8 @@
}
private fun updateRepos(newInfos: List<SubscriptionModel>) {
- dropUnusedReposFromCache(newInfos)
subIdRepositoryCache.forEach { (subId, repo) ->
- repo.setIsCarrierMerged(isCarrierMerged(subId))
+ repo.get()?.setIsCarrierMerged(isCarrierMerged(subId))
}
}
@@ -384,13 +398,6 @@
return subId == carrierMergedSubId.value
}
- private fun dropUnusedReposFromCache(newInfos: List<SubscriptionModel>) {
- // Remove any connection repository from the cache that isn't in the new set of IDs. They
- // will get garbage collected once their subscribers go away
- subIdRepositoryCache =
- subIdRepositoryCache.filter { checkSub(it.key, newInfos) }.toMutableMap()
- }
-
/**
* True if the checked subId is in the list of current subs or the active mobile data subId
*
@@ -422,6 +429,22 @@
profileClass = profileClass,
)
+ override fun dump(pw: PrintWriter, args: Array<String>) {
+ val ipw = IndentingPrintWriter(pw, " ")
+ ipw.println("Connection cache:")
+
+ ipw.increaseIndent()
+ subIdRepositoryCache.entries.forEach { (subId, repo) ->
+ ipw.println("$subId: ${repo.get()}")
+ }
+ ipw.decreaseIndent()
+
+ ipw.println("Connections (${subIdRepositoryCache.size} total):")
+ ipw.increaseIndent()
+ subIdRepositoryCache.values.forEach { it.get()?.dump(ipw) }
+ ipw.decreaseIndent()
+ }
+
companion object {
private const val LOGGING_PREFIX = "Repo"
}
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 9d194cf..ed9e405 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
@@ -35,25 +35,18 @@
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.util.kotlin.pairwiseBy
-import kotlin.time.DurationUnit
-import kotlin.time.toDuration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
interface MobileIconInteractor {
/** The table log created for this connection */
@@ -269,43 +262,6 @@
MutableStateFlow(false).asStateFlow()
}
- private val hysteresisActive = MutableStateFlow(false)
-
- private val isNonTerrestrialWithHysteresis: StateFlow<Boolean> =
- combine(isNonTerrestrial, hysteresisActive) { isNonTerrestrial, hysteresisActive ->
- if (hysteresisActive) {
- true
- } else {
- isNonTerrestrial
- }
- }
- .logDiffsForTable(
- tableLogBuffer = tableLogBuffer,
- columnName = "isNonTerrestrialWithHysteresis",
- columnPrefix = "",
- initialValue = Flags.carrierEnabledSatelliteFlag(),
- )
- .stateIn(scope, SharingStarted.Eagerly, Flags.carrierEnabledSatelliteFlag())
-
- private val lostSatelliteConnection =
- isNonTerrestrial.pairwiseBy { old, new -> hysteresisActive.value = old && !new }
-
- init {
- scope.launch { lostSatelliteConnection.collect() }
- scope.launch {
- hysteresisActive.collectLatest {
- if (it) {
- delay(
- connectionRepository.satelliteConnectionHysteresisSeconds.value.toDuration(
- DurationUnit.SECONDS
- )
- )
- hysteresisActive.value = false
- }
- }
- }
- }
-
override val isRoaming: StateFlow<Boolean> =
combine(
connectionRepository.carrierNetworkChangeActive,
@@ -367,8 +323,11 @@
combine(
level,
isInService,
- ) { level, isInService ->
- if (isInService) level else 0
+ connectionRepository.inflateSignalStrength,
+ ) { level, isInService, inflate ->
+ if (isInService) {
+ if (inflate) level + 1 else level
+ } else 0
}
.stateIn(scope, SharingStarted.WhileSubscribed(), 0)
@@ -404,7 +363,7 @@
showExclamationMark.value,
carrierNetworkChangeActive.value,
)
- isNonTerrestrialWithHysteresis
+ isNonTerrestrial
.flatMapLatest { ntn ->
if (ntn) {
satelliteIcon
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 a0c5618..5f08afd 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
@@ -128,6 +128,8 @@
mobileGroupView,
dotView,
)
+
+ view.requestLayout()
}
}
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 eda5c44..103b0e3 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
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
-import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
+import com.android.settingslib.AccessibilityContentDescriptions
import com.android.systemui.Flags.statusBarStaticInoutIndicators
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -50,7 +50,7 @@
/** True if this view should be visible at all. */
val isVisible: StateFlow<Boolean>
val icon: Flow<SignalIconModel>
- val contentDescription: Flow<ContentDescription>
+ val contentDescription: Flow<ContentDescription?>
val roaming: Flow<Boolean>
/** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
val networkTypeIcon: Flow<Icon.Resource?>
@@ -123,7 +123,7 @@
override val icon: Flow<SignalIconModel> = vmProvider.flatMapLatest { it.icon }
- override val contentDescription: Flow<ContentDescription> =
+ override val contentDescription: Flow<ContentDescription?> =
vmProvider.flatMapLatest { it.contentDescription }
override val roaming: Flow<Boolean> = vmProvider.flatMapLatest { it.roaming }
@@ -206,12 +206,26 @@
override val icon: Flow<SignalIconModel> = iconInteractor.signalLevelIcon
- override val contentDescription: Flow<ContentDescription> = run {
- val initial = ContentDescription.Resource(PHONE_SIGNAL_STRENGTH[0])
+ override val contentDescription: Flow<ContentDescription?> =
iconInteractor.signalLevelIcon
- .map { ContentDescription.Resource(PHONE_SIGNAL_STRENGTH[it.level]) }
- .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
- }
+ .map {
+ // We expect the signal icon to be cellular here since this is the cellular vm
+ if (it !is SignalIconModel.Cellular) {
+ null
+ } else {
+ val resId =
+ AccessibilityContentDescriptions.getDescriptionForLevel(
+ it.level,
+ it.numberOfLevels
+ )
+ if (resId != 0) {
+ ContentDescription.Resource(resId)
+ } else {
+ null
+ }
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), null)
private val showNetworkTypeIcon: Flow<Boolean> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index 08ed030..054116d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -22,6 +22,8 @@
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
+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.domain.interactor.DeviceProvisioningInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -41,6 +43,7 @@
val repo: DeviceBasedSatelliteRepository,
iconsInteractor: MobileIconsInteractor,
deviceProvisioningInteractor: DeviceProvisioningInteractor,
+ wifiInteractor: WifiInteractor,
@Application scope: CoroutineScope,
) {
/** Must be observed by any UI showing Satellite iconography */
@@ -73,6 +76,9 @@
val isDeviceProvisioned: Flow<Boolean> = deviceProvisioningInteractor.isDeviceProvisioned
+ val isWifiActive: Flow<Boolean> =
+ wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active }
+
/** When all connections are considered OOS, satellite connectivity is potentially valid */
val areAllConnectionsOutOfService =
if (Flags.oemEnabledSatelliteFlag()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 40641be..a0291b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -59,9 +59,10 @@
combine(
interactor.isSatelliteAllowed,
interactor.isDeviceProvisioned,
+ interactor.isWifiActive,
airplaneModeRepository.isAirplaneMode
- ) { isSatelliteAllowed, isDeviceProvisioned, isAirplaneMode ->
- isSatelliteAllowed && isDeviceProvisioned && !isAirplaneMode
+ ) { isSatelliteAllowed, isDeviceProvisioned, isWifiActive, isAirplaneMode ->
+ isSatelliteAllowed && isDeviceProvisioned && !isWifiActive && !isAirplaneMode
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
index 52a6d8c..cc87e8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
@@ -19,6 +19,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
@@ -77,15 +80,13 @@
@Application coroutineScope: CoroutineScope,
) : CollapsedStatusBarViewModel {
override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> =
- keyguardTransitionInteractor.lockscreenToOccludedTransition
- .map {
- it.transitionState == TransitionState.STARTED ||
- it.transitionState == TransitionState.RUNNING
- }
+ keyguardTransitionInteractor
+ .isInTransition(LOCKSCREEN, OCCLUDED)
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false)
override val transitionFromLockscreenToDreamStartedEvent: Flow<Unit> =
- keyguardTransitionInteractor.lockscreenToDreamingTransition
+ keyguardTransitionInteractor
+ .transition(LOCKSCREEN, DREAMING)
.filter { it.transitionState == TransitionState.STARTED }
.map {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index 9cdecef..0c2abd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -115,13 +115,16 @@
* Run or ignore Runnable for given HeadsUpEntry. If entry was never shown, ignore and delete
* all Runnables associated with that entry.
*/
- fun delete(entry: HeadsUpEntry, runnable: Runnable, label: String) {
+ fun delete(entry: HeadsUpEntry?, runnable: Runnable, label: String) {
if (!NotificationThrottleHun.isEnabled) {
runnable.run()
return
}
val fn = "[$label] => AvalancheController.delete " + getKey(entry)
-
+ if (entry == null) {
+ log { "$fn => cannot remove NULL entry" }
+ return
+ }
if (entry in nextMap) {
log { "$fn => [remove from next]" }
if (entry in nextMap) nextMap.remove(entry)
@@ -150,18 +153,20 @@
// Use default duration, like we did before AvalancheController existed
return autoDismissMs
}
+
val showingList: MutableList<HeadsUpEntry> = mutableListOf()
headsUpEntryShowing?.let { showingList.add(it) }
+ nextList.sort()
val entryList = showingList + nextList
- if (entryList.indexOf(entry) == entryList.size - 1) {
- // Use default duration if last entry
+ val thisEntryIndex = entryList.indexOf(entry)
+ val nextEntryIndex = thisEntryIndex + 1
+
+ // If last entry, use default duration
+ if (nextEntryIndex >= entryList.size) {
return autoDismissMs
}
-
- nextList.sort()
- val nextEntry = nextList[0]
-
+ val nextEntry = entryList[nextEntryIndex]
if (nextEntry.compareNonTimeFields(entry) == -1) {
// Next entry is higher priority
return 500
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index d99af2d..b8318a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -256,10 +256,15 @@
// A copy is necessary here as we are changing the underlying map. This would cause
// undefined behavior if we iterated over the key set directly.
ArraySet<String> keysToRemove = new ArraySet<>(mHeadsUpEntryMap.keySet());
+
+ // Must get waiting keys before calling removeEntry, which clears waiting entries in
+ // AvalancheController
+ List<String> waitingKeysToRemove = mAvalancheController.getWaitingKeys();
+
for (String key : keysToRemove) {
removeEntry(key);
}
- for (String key : mAvalancheController.getWaitingKeys()) {
+ for (String key : waitingKeysToRemove) {
removeEntry(key);
}
}
@@ -903,8 +908,12 @@
mLogger.logAutoRemoveCanceled(mEntry, reason);
}
};
- mAvalancheController.update(this, runnable,
- reason + " removeAutoRemovalCallbacks");
+ if (isHeadsUpEntry(this.mEntry.getKey())) {
+ mAvalancheController.update(this, runnable, reason + " cancelAutoRemovalCallbacks");
+ } else {
+ // Just removed
+ runnable.run();
+ }
}
public void scheduleAutoRemovalCallback(FinishTimeUpdater finishTimeCalculator,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
index 52a2e9c..28a2a1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
@@ -73,7 +73,8 @@
/** Returns whether or not the given notification is managed by this manager. */
fun isHeadsUpEntry(key: String): Boolean
- fun isHeadsUpGoingAway(): Boolean
+ /** @see setHeadsUpAnimatingAway */
+ fun isHeadsUpAnimatingAwayValue(): Boolean
/** Returns if the given notification is snoozed or not. */
fun isSnoozed(packageName: String): Boolean
@@ -130,7 +131,7 @@
* Set that we are exiting the headsUp pinned mode, but some notifications might still be
* animating out. This is used to keep the touchable regions in a reasonable state.
*/
- fun setHeadsUpGoingAway(headsUpGoingAway: Boolean)
+ fun setHeadsUpAnimatingAway(headsUpAnimatingAway: Boolean)
/**
* Notifies that a remote input textbox in notification gets active or inactive.
@@ -194,10 +195,10 @@
interface OnHeadsUpPhoneListenerChange {
/**
* Called when a heads up notification is 'going away' or no longer 'going away'. See
- * [HeadsUpManager.setHeadsUpGoingAway].
+ * [HeadsUpManager.setHeadsUpAnimatingAway].
*/
// TODO(b/325936094) delete this callback, and listen to the flow instead
- fun onHeadsUpGoingAwayStateChanged(headsUpGoingAway: Boolean)
+ fun onHeadsUpAnimatingAwayStateChanged(headsUpAnimatingAway: Boolean)
}
/* No op impl of HeadsUpManager. */
@@ -215,7 +216,7 @@
override fun getTopEntry() = null
override fun hasPinnedHeadsUp() = false
override fun isHeadsUpEntry(key: String) = false
- override fun isHeadsUpGoingAway() = false
+ override fun isHeadsUpAnimatingAwayValue() = false
override fun isSnoozed(packageName: String) = false
override fun isSticky(key: String?) = false
override fun isTrackingHeadsUp() = false
@@ -228,7 +229,7 @@
override fun setAnimationStateHandler(handler: AnimationStateHandler) {}
override fun setExpanded(entry: NotificationEntry, expanded: Boolean) {}
override fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean) {}
- override fun setHeadsUpGoingAway(headsUpGoingAway: Boolean) {}
+ override fun setHeadsUpAnimatingAway(headsUpAnimatingAway: Boolean) {}
override fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean) {}
override fun setTrackingHeadsUp(tracking: Boolean) {}
override fun setUser(user: Int) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index a306606..11cbc9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -176,7 +176,7 @@
bool1 = alert
bool2 = hasEntry
}, {
- "request: update notification $str1 alert: $bool1 hasEntry: $bool2 reason: $str2"
+ "request: update notification $str1 alert: $bool1 hasEntry: $bool2"
})
}
@@ -186,7 +186,7 @@
bool1 = alert
bool2 = hasEntry
}, {
- "update notification $str1 alert: $bool1 hasEntry: $bool2 reason: $str2"
+ "update notification $str1 alert: $bool1 hasEntry: $bool2"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 3522850..37ef1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -32,8 +32,6 @@
import com.android.systemui.unfold.data.repository.FoldStateRepositoryImpl
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
-import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
-import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractorImpl
import com.android.systemui.unfold.system.SystemUnfoldSharedModule
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.FoldStateProvider
@@ -186,8 +184,6 @@
interface Bindings {
@Binds fun bindRepository(impl: UnfoldTransitionRepositoryImpl): UnfoldTransitionRepository
- @Binds fun bindInteractor(impl: UnfoldTransitionInteractorImpl): UnfoldTransitionInteractor
-
@Binds fun bindFoldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt
index 0d3682c..fbbd2b9 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt
@@ -15,9 +15,11 @@
*/
package com.android.systemui.unfold.data.repository
+import androidx.annotation.FloatRange
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished
+import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionInProgress
import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionStarted
import com.android.systemui.util.kotlin.getOrNull
import java.util.Optional
@@ -42,6 +44,10 @@
sealed class UnfoldTransitionStatus {
/** Status that is sent when fold or unfold transition is in started state */
data object TransitionStarted : UnfoldTransitionStatus()
+ /** Status that is sent while fold or unfold transition is in progress */
+ data class TransitionInProgress(
+ @FloatRange(from = 0.0, to = 1.0) val progress: Float,
+ ) : UnfoldTransitionStatus()
/** Status that is sent when fold or unfold transition is finished */
data object TransitionFinished : UnfoldTransitionStatus()
}
@@ -66,6 +72,10 @@
trySend(TransitionStarted)
}
+ override fun onTransitionProgress(progress: Float) {
+ trySend(TransitionInProgress(progress))
+ }
+
override fun onTransitionFinished() {
trySend(TransitionFinished)
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
index 3e2e564..03499cb 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
@@ -15,40 +15,79 @@
*/
package com.android.systemui.unfold.domain.interactor
+import android.view.View
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished
+import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionInProgress
import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionStarted
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
/**
* Contains business-logic related to fold-unfold transitions while interacting with
* [UnfoldTransitionRepository]
*/
-interface UnfoldTransitionInteractor {
+@SysUISingleton
+class UnfoldTransitionInteractor
+@Inject
+constructor(
+ private val repository: UnfoldTransitionRepository,
+ private val configurationInteractor: ConfigurationInteractor,
+) {
/** Returns availability of fold/unfold transitions on the device */
val isAvailable: Boolean
-
- /** Suspends and waits for a fold/unfold transition to finish */
- suspend fun waitForTransitionFinish()
-
- /** Suspends and waits for a fold/unfold transition to start */
- suspend fun waitForTransitionStart()
-}
-
-class UnfoldTransitionInteractorImpl
-@Inject
-constructor(private val repository: UnfoldTransitionRepository) : UnfoldTransitionInteractor {
-
- override val isAvailable: Boolean
get() = repository.isAvailable
- override suspend fun waitForTransitionFinish() {
+ /**
+ * This mapping emits 1 when the device is completely unfolded and 0.0 when the device is
+ * completely folded.
+ */
+ private val unfoldProgress: Flow<Float> =
+ repository.transitionStatus
+ .map { (it as? TransitionInProgress)?.progress ?: 1f }
+ .onStart { emit(1f) }
+ .distinctUntilChanged()
+
+ /**
+ * Amount of X-axis translation to apply to various elements as the unfolded foldable is folded
+ * slightly, in pixels.
+ *
+ * @param isOnStartSide Whether the consumer wishes to get a translation amount that's suitable
+ * for an element that's on the start-side (left hand-side in left-to-right layouts); if
+ * `true`, the values will provide positive translations to push the left-hand-side element
+ * towards the foldable hinge; if `false`, the values will be inverted to provide negative
+ * translations to push the right-hand-side element towards the foldable hinge. Note that this
+ * method already accounts for left-to-right vs. right-to-left layout directions.
+ */
+ fun unfoldTranslationX(isOnStartSide: Boolean): Flow<Float> {
+ return combine(
+ unfoldProgress,
+ configurationInteractor.dimensionPixelSize(R.dimen.notification_side_paddings),
+ configurationInteractor.layoutDirection.map {
+ if (it == View.LAYOUT_DIRECTION_RTL) -1 else 1
+ },
+ ) { unfoldedAmount, max, layoutDirectionMultiplier ->
+ val sideMultiplier = if (isOnStartSide) 1 else -1
+ max * (1 - unfoldedAmount) * sideMultiplier * layoutDirectionMultiplier
+ }
+ }
+
+ /** Suspends and waits for a fold/unfold transition to finish */
+ suspend fun waitForTransitionFinish() {
repository.transitionStatus.filter { it is TransitionFinished }.first()
}
- override suspend fun waitForTransitionStart() {
+ /** Suspends and waits for a fold/unfold transition to start */
+ suspend fun waitForTransitionStart() {
repository.transitionStatus.filter { it is TransitionStarted }.first()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/ReduceBrightColorsControllerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/ReduceBrightColorsControllerExt.kt
new file mode 100644
index 0000000..ee00e8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/ReduceBrightColorsControllerExt.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.kotlin
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.qs.ReduceBrightColorsController
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.onStart
+
+fun ReduceBrightColorsController.isEnabled(): Flow<Boolean> {
+ return conflatedCallbackFlow {
+ val callback =
+ object : ReduceBrightColorsController.Listener {
+ override fun onActivated(activated: Boolean) {
+ trySend(activated)
+ }
+ }
+ addCallback(callback)
+ awaitClose { removeCallback(callback) }
+ }
+ .onStart { emit(isReduceBrightColorsActivated) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Util.java b/packages/SystemUI/src/com/android/systemui/volume/Util.java
index 7edb5a5..df19013 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Util.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Util.java
@@ -17,6 +17,7 @@
package com.android.systemui.volume;
import android.media.AudioManager;
+import android.util.MathUtils;
import android.view.View;
/**
@@ -46,4 +47,27 @@
if (v == null || (v.getVisibility() == View.VISIBLE) == vis) return;
v.setVisibility(vis ? View.VISIBLE : View.GONE);
}
+
+ /**
+ * Translates a value from one range to another.
+ *
+ * ```
+ * Given: currentValue=3, currentRange=[0, 8], targetRange=[0, 100]
+ * Result: 37.5
+ * ```
+ */
+ public static float translateToRange(float value,
+ float valueRangeStart,
+ float valueRangeEnd,
+ float targetRangeStart,
+ float targetRangeEnd) {
+ float currentRangeLength = valueRangeEnd - valueRangeStart;
+ float targetRangeLength = targetRangeEnd - targetRangeStart;
+ if (currentRangeLength == 0f || targetRangeLength == 0f) {
+ return targetRangeStart;
+ }
+ float valueFraction = (value - valueRangeStart) / currentRangeLength;
+ return MathUtils.constrain(targetRangeStart + valueFraction * targetRangeLength,
+ targetRangeStart, targetRangeEnd);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 27a708a..2245541 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -137,13 +137,13 @@
import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
import com.android.systemui.volume.ui.navigation.VolumeNavigator;
-import dagger.Lazy;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
+import dagger.Lazy;
+
/**
* Visual presentation of the volume dialog.
*
@@ -166,6 +166,7 @@
private static final int DRAWER_ANIMATION_DURATION_SHORT = 175;
private static final int DRAWER_ANIMATION_DURATION = 250;
+ private static final int DISPLAY_RANGE_MULTIPLIER = 100;
/** Shows volume dialog show animation. */
private static final String TYPE_SHOW = "show";
@@ -826,12 +827,14 @@
writer.print(" mSilentMode: "); writer.println(mSilentMode);
}
- private static int getImpliedLevel(SeekBar seekBar, int progress) {
- final int m = seekBar.getMax();
- final int n = m / 100 - 1;
- final int level = progress == 0 ? 0
- : progress == m ? (m / 100) : (1 + (int) ((progress / (float) m) * n));
- return level;
+ private static int getVolumeFromProgress(StreamState state, SeekBar seekBar, int progress) {
+ return (int) Util.translateToRange(progress, seekBar.getMin(), seekBar.getMax(),
+ state.levelMin, state.levelMax);
+ }
+
+ private static int getProgressFromVolume(StreamState state, SeekBar seekBar, int volume) {
+ return (int) Util.translateToRange(volume, state.levelMin, state.levelMax, seekBar.getMin(),
+ seekBar.getMax());
}
@SuppressLint("InflateParams")
@@ -854,6 +857,8 @@
addSliderHapticsToRow(row);
row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
row.number = row.view.findViewById(R.id.volume_number);
+ row.slider.setAccessibilityDelegate(
+ new VolumeDialogSeekBarAccessibilityDelegate(DISPLAY_RANGE_MULTIPLIER));
row.anim = null;
@@ -1129,7 +1134,9 @@
}
updateSelectedRingerContainerDescription(true);
-
+ mSelectedRingerContainer.setImportantForAccessibility(
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ mSelectedRingerContainer.clearFocus();
mIsRingerDrawerOpen = true;
}
@@ -1175,7 +1182,8 @@
.start();
updateSelectedRingerContainerDescription(false);
-
+ mSelectedRingerContainer.setImportantForAccessibility(
+ View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
mIsRingerDrawerOpen = false;
}
@@ -1746,7 +1754,7 @@
boolean isZenMuted = mState.zenMode == Global.ZEN_MODE_ALARMS
|| mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
|| (mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && mState.disallowRinger);
+ && mState.disallowRinger);
enableRingerViewsH(!isZenMuted);
switch (mState.ringerModeInternal) {
case AudioManager.RINGER_MODE_VIBRATE:
@@ -1796,7 +1804,7 @@
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLICK, hintLabel));
+ AccessibilityNodeInfo.ACTION_CLICK, hintLabel));
}
});
}
@@ -1913,12 +1921,12 @@
: false;
// update slider max
- final int max = ss.levelMax * 100;
+ final int max = ss.levelMax * DISPLAY_RANGE_MULTIPLIER;
if (max != row.slider.getMax()) {
row.slider.setMax(max);
}
// update slider min
- final int min = ss.levelMin * 100;
+ final int min = ss.levelMin * DISPLAY_RANGE_MULTIPLIER;
if (min != row.slider.getMin()) {
row.slider.setMin(min);
}
@@ -2066,7 +2074,7 @@
return; // don't update if user is sliding
}
final int progress = row.slider.getProgress();
- final int level = getImpliedLevel(row.slider, progress);
+ final int level = getVolumeFromProgress(row.ss, row.slider, progress);
final boolean rowVisible = row.view.getVisibility() == VISIBLE;
final boolean inGracePeriod = (SystemClock.uptimeMillis() - row.userAttempt)
< USER_ATTEMPT_GRACE_PERIOD;
@@ -2082,7 +2090,7 @@
return; // don't clamp if visible
}
}
- final int newProgress = vlevel * 100;
+ final int newProgress = getProgressFromVolume(row.ss, row.slider, vlevel);
if (progress != newProgress) {
if (mShowing && rowVisible) {
// animate!
@@ -2527,13 +2535,13 @@
+ " onProgressChanged " + progress + " fromUser=" + fromUser);
if (!fromUser) return;
if (mRow.ss.levelMin > 0) {
- final int minProgress = mRow.ss.levelMin * 100;
+ final int minProgress = getProgressFromVolume(mRow.ss, seekBar, mRow.ss.levelMin);
if (progress < minProgress) {
seekBar.setProgress(minProgress);
progress = minProgress;
}
}
- final int userLevel = getImpliedLevel(seekBar, progress);
+ final int userLevel = getVolumeFromProgress(mRow.ss, seekBar, progress);
if (mRow.ss.level != userLevel || mRow.ss.muted && userLevel > 0) {
mRow.userAttempt = SystemClock.uptimeMillis();
if (mRow.requestedLevel != userLevel) {
@@ -2566,7 +2574,7 @@
}
mRow.tracking = false;
mRow.userAttempt = SystemClock.uptimeMillis();
- final int userLevel = getImpliedLevel(seekBar, seekBar.getProgress());
+ final int userLevel = getVolumeFromProgress(mRow.ss, seekBar, seekBar.getProgress());
Events.writeEvent(Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel);
if (mRow.ss.level != userLevel) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(H.RECHECK, mRow),
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogSeekBarAccessibilityDelegate.kt b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogSeekBarAccessibilityDelegate.kt
new file mode 100644
index 0000000..cd31a95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogSeekBarAccessibilityDelegate.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume
+
+import android.os.Bundle
+import android.view.View
+import android.view.View.AccessibilityDelegate
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.SeekBar
+import com.android.internal.R
+
+class VolumeDialogSeekBarAccessibilityDelegate(
+ private val accessibilityStep: Int,
+) : AccessibilityDelegate() {
+
+ override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean {
+ require(host is SeekBar) { "This class only works with the SeekBar" }
+ val seekBar: SeekBar = host
+ if (
+ action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD ||
+ action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
+ ) {
+ var increment = accessibilityStep
+ if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
+ increment = -increment
+ }
+
+ return super.performAccessibilityAction(
+ host,
+ R.id.accessibilityActionSetProgress,
+ Bundle().apply {
+ putFloat(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE,
+ (seekBar.progress + increment).coerceIn(seekBar.min, seekBar.max).toFloat(),
+ )
+ },
+ )
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/UiEventLoggerStartableModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/UiEventLoggerStartableModule.kt
new file mode 100644
index 0000000..9b84090
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/UiEventLoggerStartableModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dagger
+
+import com.android.systemui.volume.domain.startable.AudioModeLoggerStartable
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+interface UiEventLoggerStartableModule {
+
+ @Binds
+ @IntoSet
+ fun bindAudioModeLoggerStartable(
+ audioModeLoggerStartable: AudioModeLoggerStartable,
+ ): VolumePanelStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartable.kt
new file mode 100644
index 0000000..1244757
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartable.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.domain.startable
+
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.launch
+
+/** Logger for audio mode */
+@VolumePanelScope
+class AudioModeLoggerStartable
+@Inject
+constructor(
+ @VolumePanelScope private val scope: CoroutineScope,
+ private val uiEventLogger: UiEventLogger,
+ private val audioModeInteractor: AudioModeInteractor,
+) : VolumePanelStartable {
+
+ override fun start() {
+ scope.launch {
+ audioModeInteractor.isOngoingCall.distinctUntilChanged().collect { ongoingCall ->
+ uiEventLogger.log(
+ if (ongoingCall) VolumePanelUiEvent.VOLUME_PANEL_AUDIO_MODE_CHANGE_TO_CALLING
+ else VolumePanelUiEvent.VOLUME_PANEL_AUDIO_MODE_CHANGE_TO_NORMAL
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
index 8f18aa8..8ce3b1f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
@@ -41,12 +41,14 @@
interface AncSliceRepository {
/**
- * ANC slice with a given width. Emits null when there is no ANC slice available. This can mean
- * that:
+ * ANC slice with a given width. [isCollapsed] slice shows a single button, and expanded shows a
+ * row buttons.
+ *
+ * Emits null when there is no ANC slice available. This can mean that:
* - there is no supported device connected;
* - there is no slice provider for the uri;
*/
- fun ancSlice(width: Int): Flow<Slice?>
+ fun ancSlice(width: Int, isCollapsed: Boolean, hideLabel: Boolean): Flow<Slice?>
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -60,9 +62,14 @@
private val localMediaRepository = mediaRepositoryFactory.create(null)
- override fun ancSlice(width: Int): Flow<Slice?> {
+ override fun ancSlice(width: Int, isCollapsed: Boolean, hideLabel: Boolean): Flow<Slice?> {
return localMediaRepository.currentConnectedDevice
- .map { (it as? BluetoothMediaDevice)?.cachedDevice?.device?.getExtraControlUri(width) }
+ .map {
+ (it as? BluetoothMediaDevice)
+ ?.cachedDevice
+ ?.device
+ ?.getExtraControlUri(width, isCollapsed, hideLabel)
+ }
.distinctUntilChanged()
.flatMapLatest { sliceUri ->
sliceUri ?: return@flatMapLatest flowOf(null)
@@ -71,7 +78,11 @@
.flowOn(backgroundCoroutineContext)
}
- private fun BluetoothDevice.getExtraControlUri(width: Int): Uri? {
+ private fun BluetoothDevice.getExtraControlUri(
+ width: Int,
+ isCollapsed: Boolean,
+ hideLabel: Boolean
+ ): Uri? {
val uri: String? = BluetoothUtils.getControlUriMetaData(this)
uri ?: return null
@@ -81,7 +92,8 @@
Uri.parse(
"$uri$width" +
"&version=${SliceParameters.VERSION}" +
- "&is_collapsed=${SliceParameters.IS_COLLAPSED}"
+ "&is_collapsed=$isCollapsed" +
+ "&hide_label=$hideLabel"
)
}
}
@@ -98,11 +110,5 @@
* 2) new slice
*/
const val VERSION = 2
-
- /**
- * Collapsed slice shows a single button, and expanded shows a row buttons. Supported since
- * [VERSION]==2.
- */
- const val IS_COLLAPSED = false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt
index 89b9274..dc4be26 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.anc.domain
import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
+import com.android.systemui.volume.panel.component.anc.domain.model.AncSlices
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
import javax.inject.Inject
@@ -31,5 +32,6 @@
private val ancSliceInteractor: AncSliceInteractor,
) : ComponentAvailabilityCriteria {
- override fun isAvailable(): Flow<Boolean> = ancSliceInteractor.ancSlice.map { it != null }
+ override fun isAvailable(): Flow<Boolean> =
+ ancSliceInteractor.ancSlices.map { it is AncSlices.Ready }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
index 91af622..cefa269 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
@@ -20,16 +20,19 @@
import android.app.slice.SliceItem.FORMAT_SLICE
import androidx.slice.Slice
import com.android.systemui.volume.panel.component.anc.data.repository.AncSliceRepository
+import com.android.systemui.volume.panel.component.anc.domain.model.AncSlices
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
/** Provides a valid slice from [AncSliceRepository]. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -41,25 +44,35 @@
scope: CoroutineScope,
) {
- // Start with a positive width to check is the Slice is available.
- private val width = MutableStateFlow(1)
+ // Any positive width to check if the Slice is available.
+ private val buttonSliceWidth = MutableStateFlow(1)
+ private val popupSliceWidth = MutableStateFlow(1)
- /** Provides a valid ANC slice. */
- val ancSlice: SharedFlow<Slice?> =
- width
- .flatMapLatest { width -> ancSliceRepository.ancSlice(width) }
- .map { slice ->
- if (slice?.isValidSlice() == true) {
- slice
+ val ancSlices: StateFlow<AncSlices> =
+ combine(
+ buttonSliceWidth.flatMapLatest {
+ ancSlice(width = it, isCollapsed = true, hideLabel = true)
+ },
+ popupSliceWidth.flatMapLatest {
+ ancSlice(width = it, isCollapsed = false, hideLabel = false)
+ }
+ ) { buttonSlice, popupSlice ->
+ if (buttonSlice != null && popupSlice != null) {
+ AncSlices.Ready(buttonSlice = buttonSlice, popupSlice = popupSlice)
} else {
- null
+ AncSlices.Unavailable
}
}
- .shareIn(scope, SharingStarted.Eagerly, replay = 1)
+ .stateIn(scope, SharingStarted.Eagerly, AncSlices.Unavailable)
- /** Updates the width of the [ancSlice] */
- fun changeWidth(newWidth: Int) {
- width.value = newWidth
+ /**
+ * Provides a valid [isCollapsed] ANC slice for a given [width]. Use [hideLabel] == true to
+ * remove the labels from the [Slice].
+ */
+ private fun ancSlice(width: Int, isCollapsed: Boolean, hideLabel: Boolean): Flow<Slice?> {
+ return ancSliceRepository
+ .ancSlice(width = width, isCollapsed = isCollapsed, hideLabel = hideLabel)
+ .filter { it?.isValidSlice() != false }
}
private fun Slice.isValidSlice(): Boolean {
@@ -73,4 +86,20 @@
}
return false
}
+
+ /**
+ * Call this to update [AncSlices.Ready.popupSlice] width in a reaction to container size
+ * change.
+ */
+ fun onPopupSliceWidthChanged(width: Int) {
+ popupSliceWidth.tryEmit(width)
+ }
+
+ /**
+ * Call this to update [AncSlices.Ready.buttonSlice] width in a reaction to container size
+ * change.
+ */
+ fun onButtonSliceWidthChanged(width: Int) {
+ buttonSliceWidth.tryEmit(width)
+ }
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/model/AncSlices.kt
similarity index 63%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/model/AncSlices.kt
index f6140f5..3cd4e67 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/model/AncSlices.kt
@@ -14,9 +14,18 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.volume.panel.component.anc.domain.model
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import androidx.slice.Slice
+
+/** Modes current ANC slices state */
+sealed interface AncSlices {
+
+ data class Ready(
+ val popupSlice: Slice,
+ val buttonSlice: Slice,
+ ) : AncSlices
+
+ /** Couldn't one or both slices. */
+ data object Unavailable : AncSlices
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
index eb96f6c..bee79bb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
@@ -16,52 +16,56 @@
package com.android.systemui.volume.panel.component.anc.ui.viewmodel
-import android.content.Context
import androidx.slice.Slice
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.res.R
+import com.android.systemui.volume.panel.component.anc.domain.AncAvailabilityCriteria
import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
-import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
+import com.android.systemui.volume.panel.component.anc.domain.model.AncSlices
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
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.filterIsInstance
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/** Volume Panel ANC component view model. */
+@OptIn(ExperimentalCoroutinesApi::class)
@VolumePanelScope
class AncViewModel
@Inject
constructor(
- @Application private val context: Context,
@VolumePanelScope private val coroutineScope: CoroutineScope,
private val interactor: AncSliceInteractor,
+ private val availabilityCriteria: AncAvailabilityCriteria,
) {
- /** ANC [Slice]. Null when there is no slice available for ANC. */
- val slice: StateFlow<Slice?> =
- interactor.ancSlice.stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ val isAvailable: Flow<Boolean>
+ get() = availabilityCriteria.isAvailable()
- /**
- * ButtonViewModel to be shown in the VolumePanel. Null when there is no ANC Slice available.
- */
- val button: StateFlow<ButtonViewModel?> =
- interactor.ancSlice
- .map { slice ->
- slice?.let {
- ButtonViewModel(
- Icon.Resource(R.drawable.ic_noise_aware, null),
- context.getString(R.string.volume_panel_noise_control_title)
- )
- }
- }
+ /** ANC [Slice]. Null when there is no slice available for ANC. */
+ val popupSlice: StateFlow<Slice?> =
+ interactor.ancSlices
+ .filterIsInstance<AncSlices.Ready>()
+ .map { it.popupSlice }
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
- /** Call this to update [slice] width in a reaction to container size change. */
- fun changeSliceWidth(width: Int) {
- interactor.changeWidth(width)
+ /** Button [Slice] to be shown in the VolumePanel. Null when there is no ANC Slice available. */
+ val buttonSlice: StateFlow<Slice?> =
+ interactor.ancSlices
+ .filterIsInstance<AncSlices.Ready>()
+ .map { it.buttonSlice }
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+
+ /** Call this to update [popupSlice] width in a reaction to container size change. */
+ fun onPopupSliceWidthChanged(width: Int) {
+ interactor.onPopupSliceWidthChanged(width)
+ }
+
+ /** Call this to update [buttonSlice] width in a reaction to container size change. */
+ fun onButtonSliceWidthChanged(width: Int) {
+ interactor.onButtonSliceWidthChanged(width)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModel.kt
index 04d7b1f..3ca9cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModel.kt
@@ -18,8 +18,10 @@
import android.content.Intent
import android.provider.Settings
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
import javax.inject.Inject
@@ -29,6 +31,7 @@
constructor(
private val activityStarter: ActivityStarter,
private val volumePanelViewModel: VolumePanelViewModel,
+ private val uiEventLogger: UiEventLogger,
) {
fun onDoneClicked() {
@@ -36,6 +39,7 @@
}
fun onSettingsClicked() {
+ uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_SOUND_SETTINGS_CLICKED)
activityStarter.startActivityDismissingKeyguard(
/* intent = */ Intent(Settings.ACTION_SOUND_SETTINGS),
/* onlyProvisioned = */ false,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt
index 754d258..4d11f44 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt
@@ -1,17 +1,17 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * 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
+ * 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.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.android.systemui.volume.panel.component.button.ui.viewmodel
@@ -22,4 +22,5 @@
data class ButtonViewModel(
val icon: Icon,
val label: CharSequence,
+ val isActive: Boolean = true,
)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt
deleted file mode 100644
index 6c47aec..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume.panel.component.button.ui.viewmodel
-
-import com.android.systemui.common.shared.model.Icon
-
-data class ToggleButtonViewModel(
- val isChecked: Boolean,
- val icon: Icon,
- val label: CharSequence,
-)
-
-fun ToggleButtonViewModel.toButtonViewModel(): ButtonViewModel =
- ButtonViewModel(icon = icon, label = label)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt
index aab825f..85da1d0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt
@@ -16,18 +16,36 @@
package com.android.systemui.volume.panel.component.captioning.domain
+import com.android.internal.logging.UiEventLogger
import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.shareIn
@VolumePanelScope
class CaptioningAvailabilityCriteria
@Inject
-constructor(private val captioningInteractor: CaptioningInteractor) :
- ComponentAvailabilityCriteria {
+constructor(
+ captioningInteractor: CaptioningInteractor,
+ @VolumePanelScope private val scope: CoroutineScope,
+ private val uiEventLogger: UiEventLogger,
+) : ComponentAvailabilityCriteria {
- override fun isAvailable(): Flow<Boolean> =
+ private val availability =
captioningInteractor.isSystemAudioCaptioningUiEnabled
+ .onEach { visible ->
+ uiEventLogger.log(
+ if (visible) VolumePanelUiEvent.VOLUME_PANEL_LIVE_CAPTION_TOGGLE_SHOWN
+ else VolumePanelUiEvent.VOLUME_PANEL_LIVE_CAPTION_TOGGLE_GONE
+ )
+ }
+ .shareIn(scope, SharingStarted.WhileSubscribed(), replay = 1)
+
+ override fun isAvailable(): Flow<Boolean> = availability
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
index 92f8f22..ca5aef8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
@@ -17,11 +17,13 @@
package com.android.systemui.volume.panel.component.captioning.ui.viewmodel
import android.content.Context
+import com.android.internal.logging.UiEventLogger
import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.res.R
-import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -38,13 +40,14 @@
private val context: Context,
private val captioningInteractor: CaptioningInteractor,
@VolumePanelScope private val coroutineScope: CoroutineScope,
+ private val uiEventLogger: UiEventLogger,
) {
- val buttonViewModel: StateFlow<ToggleButtonViewModel?> =
+ val buttonViewModel: StateFlow<ButtonViewModel?> =
captioningInteractor.isSystemAudioCaptioningEnabled
.map { isEnabled ->
- ToggleButtonViewModel(
- isChecked = isEnabled,
+ ButtonViewModel(
+ isActive = isEnabled,
icon =
Icon.Resource(
if (isEnabled) R.drawable.ic_volume_odi_captions
@@ -57,6 +60,13 @@
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
fun setIsSystemAudioCaptioningEnabled(enabled: Boolean) {
+ uiEventLogger.logWithPosition(
+ VolumePanelUiEvent.VOLUME_PANEL_LIVE_CAPTION_TOGGLE_CLICKED,
+ 0,
+ null,
+ if (enabled) VolumePanelUiEvent.LIVE_CAPTION_TOGGLE_ENABLED
+ else VolumePanelUiEvent.LIVE_CAPTION_TOGGLE_DISABLED
+ )
coroutineScope.launch { captioningInteractor.setIsSystemAudioCaptioningEnabled(enabled) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
index fc9602e..6b237f8e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel
import android.content.Context
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Color
import com.android.systemui.common.shared.model.Icon
@@ -26,6 +27,7 @@
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlayback
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -48,6 +50,7 @@
private val actionsInteractor: MediaOutputActionsInteractor,
private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
interactor: MediaOutputInteractor,
+ private val uiEventLogger: UiEventLogger,
) {
private val sessionWithPlayback: StateFlow<SessionWithPlayback?> =
@@ -126,6 +129,7 @@
)
fun onBarClick(expandable: Expandable) {
+ uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_MEDIA_OUTPUT_CLICKED)
actionsInteractor.onBarClick(sessionWithPlayback.value, expandable)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt
index 9f9275b..f7a602e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt
@@ -16,13 +16,10 @@
package com.android.systemui.volume.panel.component.spatial.ui.viewmodel
-import com.android.systemui.common.shared.model.Color
-import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
data class SpatialAudioButtonViewModel(
val model: SpatialAudioEnabledModel,
- val button: ToggleButtonViewModel,
- val iconColor: Color,
- val labelColor: Color,
+ val button: ButtonViewModel,
)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
index f022039..4b2d26a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
@@ -17,18 +17,17 @@
package com.android.systemui.volume.panel.component.spatial.ui.viewmodel
import android.content.Context
-import com.android.systemui.common.shared.model.Color
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
-import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel
-import com.android.systemui.volume.panel.component.button.ui.viewmodel.toButtonViewModel
import com.android.systemui.volume.panel.component.spatial.domain.SpatialAudioAvailabilityCriteria
import com.android.systemui.volume.panel.component.spatial.domain.interactor.SpatialAudioComponentInteractor
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -46,13 +45,14 @@
@VolumePanelScope private val scope: CoroutineScope,
availabilityCriteria: SpatialAudioAvailabilityCriteria,
private val interactor: SpatialAudioComponentInteractor,
+ private val uiEventLogger: UiEventLogger,
) {
val spatialAudioButton: StateFlow<ButtonViewModel?> =
interactor.isEnabled
.map {
- it.toViewModel(true)
- .toButtonViewModel()
+ val isChecked = it is SpatialAudioEnabledModel.SpatialAudioEnabled
+ it.toViewModel(isChecked)
.copy(label = context.getString(R.string.volume_panel_spatial_audio_title))
}
.stateIn(scope, SharingStarted.Eagerly, null)
@@ -74,56 +74,49 @@
}
.map { isEnabled ->
val isChecked = isEnabled == currentIsEnabled
- val buttonViewModel: ToggleButtonViewModel =
- isEnabled.toViewModel(isChecked)
- SpatialAudioButtonViewModel(
- button = buttonViewModel,
- model = isEnabled,
- iconColor =
- Color.Attribute(
- if (isChecked) {
- com.android.internal.R.attr.materialColorOnPrimaryContainer
- } else {
- com.android.internal.R.attr.materialColorOnSurfaceVariant
- }
- ),
- labelColor =
- Color.Attribute(
- if (isChecked) {
- com.android.internal.R.attr.materialColorOnSurface
- } else {
- com.android.internal.R.attr.materialColorOnSurfaceVariant
- }
- ),
- )
+ val buttonViewModel: ButtonViewModel = isEnabled.toViewModel(isChecked)
+ SpatialAudioButtonViewModel(button = buttonViewModel, model = isEnabled)
}
}
.stateIn(scope, SharingStarted.Eagerly, emptyList())
fun setEnabled(model: SpatialAudioEnabledModel) {
+ uiEventLogger.logWithPosition(
+ VolumePanelUiEvent.VOLUME_PANEL_SPATIAL_AUDIO_TOGGLE_CLICKED,
+ 0,
+ null,
+ when (model) {
+ SpatialAudioEnabledModel.Disabled -> 0
+ SpatialAudioEnabledModel.SpatialAudioEnabled -> 1
+ SpatialAudioEnabledModel.HeadTrackingEnabled -> 2
+ else -> {
+ -1
+ }
+ }
+ )
scope.launch { interactor.setEnabled(model) }
}
- private fun SpatialAudioEnabledModel.toViewModel(isChecked: Boolean): ToggleButtonViewModel {
+ private fun SpatialAudioEnabledModel.toViewModel(isChecked: Boolean): ButtonViewModel {
if (this is SpatialAudioEnabledModel.HeadTrackingEnabled) {
- return ToggleButtonViewModel(
- isChecked = isChecked,
+ return ButtonViewModel(
+ isActive = isChecked,
icon = Icon.Resource(R.drawable.ic_head_tracking, contentDescription = null),
label = context.getString(R.string.volume_panel_spatial_audio_tracking)
)
}
if (this is SpatialAudioEnabledModel.SpatialAudioEnabled) {
- return ToggleButtonViewModel(
- isChecked = isChecked,
+ return ButtonViewModel(
+ isActive = isChecked,
icon = Icon.Resource(R.drawable.ic_spatial_audio, contentDescription = null),
label = context.getString(R.string.volume_panel_spatial_audio_fixed)
)
}
if (this is SpatialAudioEnabledModel.Disabled) {
- return ToggleButtonViewModel(
- isChecked = isChecked,
+ return ButtonViewModel(
+ isActive = isChecked,
icon = Icon.Resource(R.drawable.ic_spatial_audio_off, contentDescription = null),
label = context.getString(R.string.volume_panel_spatial_audio_off)
)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt
deleted file mode 100644
index ecd89ea..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume.panel.component.volume.domain.interactor
-
-import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
-import javax.inject.Inject
-
-/** Converts from slider value to volume and back. */
-@VolumePanelScope
-class VolumeSliderInteractor @Inject constructor() {
-
- /** mimic percentage volume setting */
- private val displayValueRange: ClosedFloatingPointRange<Float> = 0f..100f
-
- /**
- * Translates [volume], that belongs to [volumeRange] to the value that belongs to
- * [displayValueRange].
- */
- fun processVolumeToValue(
- volume: Int,
- volumeRange: ClosedRange<Int>,
- ): Float {
- val currentRangeStart: Float = volumeRange.start.toFloat()
- val targetRangeStart: Float = displayValueRange.start
- val currentRangeLength: Float = (volumeRange.endInclusive.toFloat() - currentRangeStart)
- val targetRangeLength: Float = displayValueRange.endInclusive - targetRangeStart
- if (currentRangeLength == 0f || targetRangeLength == 0f) {
- return 0f
- }
- val volumeFraction: Float = (volume.toFloat() - currentRangeStart) / currentRangeLength
- return targetRangeStart + volumeFraction * targetRangeLength
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
index 57b5d57..c8cd6fd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -18,13 +18,14 @@
import android.content.Context
import android.media.AudioManager
+import com.android.internal.logging.UiEventLogger
import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.res.R
-import com.android.systemui.volume.panel.component.volume.domain.interactor.VolumeSliderInteractor
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -44,7 +45,7 @@
@Assisted private val coroutineScope: CoroutineScope,
private val context: Context,
private val audioVolumeInteractor: AudioVolumeInteractor,
- private val volumeSliderInteractor: VolumeSliderInteractor,
+ private val uiEventLogger: UiEventLogger,
) : SliderViewModel {
private val audioStream = audioStreamWrapper.audioStream
@@ -71,6 +72,19 @@
AudioStream(AudioManager.STREAM_ALARM) to R.string.stream_alarm_unavailable,
AudioStream(AudioManager.STREAM_MUSIC) to R.string.stream_media_unavailable,
)
+ private val uiEventByStream =
+ mapOf(
+ AudioStream(AudioManager.STREAM_MUSIC) to
+ VolumePanelUiEvent.VOLUME_PANEL_MUSIC_SLIDER_TOUCHED,
+ AudioStream(AudioManager.STREAM_VOICE_CALL) to
+ VolumePanelUiEvent.VOLUME_PANEL_VOICE_CALL_SLIDER_TOUCHED,
+ AudioStream(AudioManager.STREAM_RING) to
+ VolumePanelUiEvent.VOLUME_PANEL_RING_SLIDER_TOUCHED,
+ AudioStream(AudioManager.STREAM_NOTIFICATION) to
+ VolumePanelUiEvent.VOLUME_PANEL_NOTIFICATION_SLIDER_TOUCHED,
+ AudioStream(AudioManager.STREAM_ALARM) to
+ VolumePanelUiEvent.VOLUME_PANEL_ALARM_SLIDER_TOUCHED,
+ )
override val slider: StateFlow<SliderState> =
combine(
@@ -90,6 +104,10 @@
}
}
+ override fun onValueChangeFinished() {
+ uiEventByStream[audioStream]?.let { uiEventLogger.log(it) }
+ }
+
override fun toggleMuted(state: SliderState) {
val audioViewModel = state as? State
audioViewModel ?: return
@@ -105,10 +123,6 @@
return State(
value = volume.toFloat(),
valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
- valueText =
- SliderViewModel.formatValue(
- volumeSliderInteractor.processVolumeToValue(volume, volumeRange)
- ),
icon = getIcon(ringerMode),
label = labelsByStream[audioStream]?.let(context::getString)
?: error("No label for the stream: $audioStream"),
@@ -157,7 +171,6 @@
override val valueRange: ClosedFloatingPointRange<Float>,
override val icon: Icon,
override val label: String,
- override val valueText: String,
override val disabledMessage: String?,
override val isEnabled: Boolean,
override val a11yStep: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
index 8d8fa17..956ab66 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
@@ -22,7 +22,6 @@
import com.android.systemui.res.R
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
-import com.android.systemui.volume.panel.component.volume.domain.interactor.VolumeSliderInteractor
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -41,7 +40,6 @@
@Assisted private val coroutineScope: CoroutineScope,
private val context: Context,
private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
- private val volumeSliderInteractor: VolumeSliderInteractor,
) : SliderViewModel {
override val slider: StateFlow<SliderState> =
@@ -56,6 +54,8 @@
}
}
+ override fun onValueChangeFinished() {}
+
override fun toggleMuted(state: SliderState) {
// do nothing because this action isn't supported for Cast sliders.
}
@@ -66,13 +66,6 @@
value = currentVolume.toFloat(),
valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
icon = Icon.Resource(R.drawable.ic_cast, null),
- valueText =
- SliderViewModel.formatValue(
- volumeSliderInteractor.processVolumeToValue(
- volume = currentVolume,
- volumeRange = volumeRange,
- )
- ),
label = context.getString(R.string.media_device_cast),
isEnabled = true,
a11yStep = 1
@@ -83,13 +76,13 @@
override val value: Float,
override val valueRange: ClosedFloatingPointRange<Float>,
override val icon: Icon,
- override val valueText: String,
override val label: String,
override val isEnabled: Boolean,
override val a11yStep: Int,
) : SliderState {
override val disabledMessage: String?
get() = null
+
override val isMutable: Boolean
get() = false
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
index 8eb0b89..d71a9d8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
@@ -28,7 +28,6 @@
val valueRange: ClosedFloatingPointRange<Float>
val icon: Icon?
val isEnabled: Boolean
- val valueText: String
val label: String
/**
* A11y slider controls works by adjusting one step up or down. The default slider step isn't
@@ -42,7 +41,6 @@
override val value: Float = 0f
override val valueRange: ClosedFloatingPointRange<Float> = 0f..1f
override val icon: Icon? = null
- override val valueText: String = ""
override val label: String = ""
override val disabledMessage: String? = null
override val a11yStep: Int = 0
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
index e78f833..7ded8c5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
@@ -25,10 +25,7 @@
fun onValueChanged(state: SliderState, newValue: Float)
+ fun onValueChangeFinished()
+
fun toggleMuted(state: SliderState)
-
- companion object {
-
- fun formatValue(value: Float): String = "%.0f".format(value)
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt
index d1d5390..f889ed6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.dagger
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
import dagger.Module
@@ -31,4 +32,6 @@
@Multibinds fun criteriaMap(): Map<VolumePanelComponentKey, ComponentAvailabilityCriteria>
@Multibinds fun components(): Map<VolumePanelComponentKey, VolumePanelUiComponent>
+
+ @Multibinds fun startables(): Set<VolumePanelStartable>
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
index d868c33..ec64f3d9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
@@ -16,6 +16,7 @@
package com.android.systemui.volume.panel.dagger
+import com.android.systemui.volume.dagger.UiEventLoggerStartableModule
import com.android.systemui.volume.panel.component.anc.AncModule
import com.android.systemui.volume.panel.component.bottombar.BottomBarModule
import com.android.systemui.volume.panel.component.captioning.CaptioningModule
@@ -25,6 +26,7 @@
import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.domain.DomainModule
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
import com.android.systemui.volume.panel.ui.UiModule
import com.android.systemui.volume.panel.ui.composable.ComponentsFactory
@@ -47,6 +49,7 @@
DefaultMultibindsModule::class,
DomainModule::class,
UiModule::class,
+ UiEventLoggerStartableModule::class,
// Components modules
BottomBarModule::class,
AncModule::class,
@@ -66,6 +69,8 @@
fun componentsLayoutManager(): ComponentsLayoutManager
+ fun volumePanelStartables(): Set<VolumePanelStartable>
+
@Subcomponent.Factory
interface Factory : VolumePanelComponentFactory {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/VolumePanelStartable.kt
similarity index 78%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/src/com/android/systemui/volume/panel/domain/VolumePanelStartable.kt
index f6140f5..9c39f5e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/VolumePanelStartable.kt
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.volume.panel.domain
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+/** Code that needs to be run when Volume Panel is started.. */
+interface VolumePanelStartable {
+ fun start()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/VolumePanelUiEvent.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/VolumePanelUiEvent.kt
new file mode 100644
index 0000000..8b8714f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/VolumePanelUiEvent.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.ui
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+/** UI events for Volume Panel. */
+enum class VolumePanelUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The volume panel is shown") VOLUME_PANEL_SHOWN(1634),
+ @UiEvent(doc = "The volume panel is gone") VOLUME_PANEL_GONE(1635),
+ @UiEvent(doc = "Media output is clicked") VOLUME_PANEL_MEDIA_OUTPUT_CLICKED(1636),
+ @UiEvent(doc = "Audio mode changed to normal") VOLUME_PANEL_AUDIO_MODE_CHANGE_TO_NORMAL(1680),
+ @UiEvent(doc = "Audio mode changed to calling") VOLUME_PANEL_AUDIO_MODE_CHANGE_TO_CALLING(1681),
+ @UiEvent(doc = "Sound settings is clicked") VOLUME_PANEL_SOUND_SETTINGS_CLICKED(1638),
+ @UiEvent(doc = "The music volume slider is touched") VOLUME_PANEL_MUSIC_SLIDER_TOUCHED(1639),
+ @UiEvent(doc = "The voice call volume slider is touched")
+ VOLUME_PANEL_VOICE_CALL_SLIDER_TOUCHED(1640),
+ @UiEvent(doc = "The ring volume slider is touched") VOLUME_PANEL_RING_SLIDER_TOUCHED(1641),
+ @UiEvent(doc = "The notification volume slider is touched")
+ VOLUME_PANEL_NOTIFICATION_SLIDER_TOUCHED(1642),
+ @UiEvent(doc = "The alarm volume slider is touched") VOLUME_PANEL_ALARM_SLIDER_TOUCHED(1643),
+ @UiEvent(doc = "Live caption toggle is shown") VOLUME_PANEL_LIVE_CAPTION_TOGGLE_SHOWN(1644),
+ @UiEvent(doc = "Live caption toggle is gone") VOLUME_PANEL_LIVE_CAPTION_TOGGLE_GONE(1645),
+ @UiEvent(doc = "Live caption toggle is clicked") VOLUME_PANEL_LIVE_CAPTION_TOGGLE_CLICKED(1646),
+ @UiEvent(doc = "Spatial audio button is shown") VOLUME_PANEL_SPATIAL_AUDIO_BUTTON_SHOWN(1647),
+ @UiEvent(doc = "Spatial audio button is gone") VOLUME_PANEL_SPATIAL_AUDIO_BUTTON_GONE(1648),
+ @UiEvent(doc = "Spatial audio popup is shown") VOLUME_PANEL_SPATIAL_AUDIO_POP_UP_SHOWN(1649),
+ @UiEvent(doc = "Spatial audio toggle is clicked")
+ VOLUME_PANEL_SPATIAL_AUDIO_TOGGLE_CLICKED(1650),
+ @UiEvent(doc = "ANC button is shown") VOLUME_PANEL_ANC_BUTTON_SHOWN(1651),
+ @UiEvent(doc = "ANC button is gone") VOLUME_PANEL_ANC_BUTTON_GONE(1652),
+ @UiEvent(doc = "ANC popup is shown") VOLUME_PANEL_ANC_POPUP_SHOWN(1653),
+ @UiEvent(doc = "ANC toggle is clicked") VOLUME_PANEL_ANC_TOGGLE_CLICKED(1654);
+
+ override fun getId() = metricId
+
+ companion object {
+ const val LIVE_CAPTION_TOGGLE_DISABLED = 0
+ const val LIVE_CAPTION_TOGGLE_ENABLED = 1
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
index c728fef..ccb91ac 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
@@ -21,8 +21,10 @@
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag
+import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import com.android.systemui.volume.panel.ui.composable.VolumePanelRoot
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
import javax.inject.Inject
@@ -34,6 +36,7 @@
private val volumePanelViewModelFactory: Provider<VolumePanelViewModel.Factory>,
private val volumePanelFlag: VolumePanelFlag,
private val configurationController: ConfigurationController,
+ private val uiEventLogger: UiEventLogger,
) : ComponentActivity() {
private val viewModel: VolumePanelViewModel by
@@ -43,8 +46,16 @@
enableEdgeToEdge()
super.onCreate(savedInstanceState)
volumePanelFlag.assertNewVolumePanel()
-
- setContent { VolumePanelRoot(viewModel = viewModel, onDismiss = ::finish) }
+ uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_SHOWN)
+ setContent {
+ VolumePanelRoot(
+ viewModel = viewModel,
+ onDismiss = {
+ uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_GONE)
+ finish()
+ }
+ )
+ }
}
override fun onContentChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
index 5ae827f..1de4fd1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
@@ -26,6 +26,7 @@
import com.android.systemui.statusbar.policy.onConfigChanged
import com.android.systemui.volume.panel.dagger.VolumePanelComponent
import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
import com.android.systemui.volume.panel.ui.composable.ComponentsFactory
import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
@@ -109,6 +110,10 @@
replay = 1,
)
+ init {
+ volumePanelComponent.volumePanelStartables().onEach(VolumePanelStartable::start)
+ }
+
fun dismissPanel() {
mutablePanelVisibility.update { false }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index e48b639..263ddc1 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -43,6 +43,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.CoreStartable;
+import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.qualifiers.Main;
@@ -55,6 +56,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.onehanded.OneHanded;
@@ -124,6 +126,8 @@
private final UserTracker mUserTracker;
private final DisplayTracker mDisplayTracker;
private final NoteTaskInitializer mNoteTaskInitializer;
+ private final CommunalTransitionViewModel mCommunalTransitionViewModel;
+ private final JavaAdapter mJavaAdapter;
private final Executor mSysUiMainExecutor;
// Listeners and callbacks. Note that we prefer member variable over anonymous class here to
@@ -187,6 +191,8 @@
UserTracker userTracker,
DisplayTracker displayTracker,
NoteTaskInitializer noteTaskInitializer,
+ CommunalTransitionViewModel communalTransitionViewModel,
+ JavaAdapter javaAdapter,
@Main Executor sysUiMainExecutor) {
mContext = context;
mShell = shell;
@@ -205,6 +211,8 @@
mUserTracker = userTracker;
mDisplayTracker = displayTracker;
mNoteTaskInitializer = noteTaskInitializer;
+ mCommunalTransitionViewModel = communalTransitionViewModel;
+ mJavaAdapter = javaAdapter;
mSysUiMainExecutor = sysUiMainExecutor;
}
@@ -381,6 +389,8 @@
void initRecentTasks(RecentTasks recentTasks) {
recentTasks.addAnimationStateListener(mSysUiMainExecutor,
mCommandQueue::onRecentsAnimationStateChanged);
+ mJavaAdapter.alwaysCollectFlow(mCommunalTransitionViewModel.getRecentsBackgroundColor(),
+ recentTasks::setTransitionBackgroundColor);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index ca55dd8..e72027a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -142,7 +142,6 @@
context.resources,
context,
mainExecutor,
- IMMEDIATE,
bgExecutor,
clockBuffers,
withDeps.featureFlags,
@@ -320,9 +319,19 @@
fun listenForDozeAmountTransition_updatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.lockscreenToAodTransition)
+ whenever(
+ keyguardTransitionInteractor.transition(
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.AOD
+ )
+ )
.thenReturn(transitionStep)
- whenever(keyguardTransitionInteractor.aodToLockscreenTransition)
+ whenever(
+ keyguardTransitionInteractor.transition(
+ KeyguardState.AOD,
+ KeyguardState.LOCKSCREEN
+ )
+ )
.thenReturn(transitionStep)
val job = underTest.listenForDozeAmountTransition(this)
@@ -362,6 +371,27 @@
}
@Test
+ fun listenForTransitionToLSFromOccluded_updatesClockDozeAmountToOne() =
+ runBlocking(IMMEDIATE) {
+ val transitionStep = MutableStateFlow(TransitionStep())
+ whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.LOCKSCREEN))
+ .thenReturn(transitionStep)
+
+ val job = underTest.listenForAnyStateToLockscreenTransition(this)
+ transitionStep.value =
+ TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED,
+ )
+ yield()
+
+ verify(animations, times(2)).doze(0f)
+
+ job.cancel()
+ }
+
+ @Test
fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
@@ -379,6 +409,27 @@
verify(animations, never()).doze(1f)
+ job.cancel()
+ }
+
+ @Test
+ fun listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmount() =
+ runBlocking(IMMEDIATE) {
+ val transitionStep = MutableStateFlow(TransitionStep())
+ whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.LOCKSCREEN))
+ .thenReturn(transitionStep)
+
+ val job = underTest.listenForAnyStateToLockscreenTransition(this)
+ transitionStep.value =
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED,
+ )
+ yield()
+
+ verify(animations, never()).doze(0f)
+
job.cancel()
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index a249961..319b615 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -205,9 +205,9 @@
when(mClockRegistry.createCurrentClock()).thenReturn(mClockController);
when(mClockEventController.getClock()).thenReturn(mClockController);
when(mSmallClockController.getConfig())
- .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false));
+ .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false, false));
when(mLargeClockController.getConfig())
- .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false));
+ .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false, false));
mSliceView = new View(getContext());
when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 9d81b96..99b5a4b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -272,9 +272,9 @@
assertEquals(View.VISIBLE, mFakeDateView.getVisibility());
when(mSmallClockController.getConfig())
- .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false));
+ .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false, true));
when(mLargeClockController.getConfig())
- .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false));
+ .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false, true));
verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
listenerArgumentCaptor.getValue().onCurrentClockChanged();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 9b5364e..7151c42 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -115,6 +115,7 @@
@Test
fun onViewAttached() {
underTest.onViewAttached()
+ verify(keyguardMessageAreaController).setIsVisible(true)
verify(keyguardMessageAreaController)
.setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false)
verify(keyguardUpdateMonitor)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index e71490c..acae913 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -103,7 +103,9 @@
@Test
fun onViewAttached() {
+ Mockito.reset(keyguardMessageAreaController)
underTest.onViewAttached()
+ Mockito.verify(keyguardMessageAreaController).setIsVisible(true)
Mockito.verify(keyguardUpdateMonitor)
.registerCallback(any(KeyguardUpdateMonitorCallback::class.java))
Mockito.verify(keyguardMessageAreaController)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 11fe862..b2828a4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -98,7 +98,7 @@
public void updatePosition_primaryClockAnimation() {
ClockController mockClock = mock(ClockController.class);
when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
- when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", false, true));
+ when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", false, true, false));
mController.updatePosition(10, 15, 20f, true);
@@ -113,7 +113,7 @@
public void updatePosition_alternateClockAnimation() {
ClockController mockClock = mock(ClockController.class);
when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
- when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", true, true));
+ when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", true, true, false));
mController.updatePosition(10, 15, 20f, true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java
index f924ab4..b09357f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java
@@ -36,6 +36,7 @@
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import android.widget.ImageView;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
@@ -51,6 +52,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -78,6 +80,7 @@
protected final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
protected @Mock DeviceEntryInteractor mDeviceEntryInteractor;
protected @Mock LockIconView mLockIconView;
+ protected @Mock ImageView mLockIcon;
protected @Mock AnimatedStateListDrawable mIconDrawable;
protected @Mock Context mContext;
protected @Mock Resources mResources;
@@ -146,8 +149,10 @@
when(mStatusBarStateController.isDozing()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
- mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
- mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+ if (!SceneContainerFlag.isEnabled()) {
+ mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+ }
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
@@ -172,8 +177,7 @@
mFeatureFlags,
mPrimaryBouncerInteractor,
mContext,
- () -> mDeviceEntryInteractor,
- mKosmos.getFakeSceneContainerFlags()
+ () -> mDeviceEntryInteractor
);
}
@@ -225,6 +229,7 @@
protected void setupLockIconViewMocks() {
when(mLockIconView.getResources()).thenReturn(mResources);
when(mLockIconView.getContext()).thenReturn(mContext);
+ when(mLockIconView.getLockIcon()).thenReturn(mLockIcon);
}
protected void resetLockIconView() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
index 8689842..255c7d9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
@@ -43,6 +43,7 @@
import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
import com.android.systemui.doze.util.BurnInHelperKt;
+import com.android.systemui.flags.EnableSceneContainer;
import com.android.systemui.statusbar.StatusBarState;
import org.junit.Test;
@@ -373,7 +374,6 @@
@Test
public void longPress_showBouncer_sceneContainerNotEnabled() {
init(/* useMigrationFlag= */ false);
- mKosmos.getFakeSceneContainerFlags().setEnabled(false);
when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
// WHEN longPress
@@ -385,9 +385,9 @@
}
@Test
+ @EnableSceneContainer
public void longPress_showBouncer() {
init(/* useMigrationFlag= */ false);
- mKosmos.getFakeSceneContainerFlags().setEnabled(true);
when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
// WHEN longPress
@@ -399,9 +399,9 @@
}
@Test
+ @EnableSceneContainer
public void longPress_falsingTriggered_doesNotShowBouncer() {
init(/* useMigrationFlag= */ false);
- mKosmos.getFakeSceneContainerFlags().setEnabled(true);
when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true);
// WHEN longPress
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
index 7c121e1..d7bd59e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
@@ -31,36 +31,57 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
-import android.testing.AndroidTestingRunner;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import androidx.test.filters.SmallTest;
import com.android.settingslib.SliceBroadcastRelay;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidTestingRunner.class)
+import java.util.List;
+
+@RunWith(Parameterized.class)
@SmallTest
public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
+ @Parameterized.Parameters(name = "{0}")
+ public static List<FlagsParameterization> getFlags() {
+ return FlagsParameterization.allCombinationsOf(
+ Flags.FLAG_SLICE_BROADCAST_RELAY_IN_BACKGROUND);
+ }
+
private static final String TEST_ACTION = "com.android.systemui.action.TEST_ACTION";
+ private final FakeExecutor mBackgroundExecutor = new FakeExecutor(new FakeSystemClock());
+
private SliceBroadcastRelayHandler mRelayHandler;
private Context mSpyContext;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
+
+ public SliceBroadcastRelayHandlerTest(FlagsParameterization flags) {
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mSpyContext = spy(mContext);
- mRelayHandler = new SliceBroadcastRelayHandler(mSpyContext, mBroadcastDispatcher);
+ mRelayHandler = new SliceBroadcastRelayHandler(mSpyContext, mBroadcastDispatcher,
+ mBackgroundExecutor);
}
@Test
@@ -80,6 +101,7 @@
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, testUri);
mRelayHandler.handleIntent(intent);
+ mBackgroundExecutor.runAllReady();
verify(mSpyContext).registerReceiver(any(), eq(value), anyInt());
}
@@ -99,12 +121,14 @@
intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
mRelayHandler.handleIntent(intent);
+ mBackgroundExecutor.runAllReady();
ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mSpyContext).registerReceiver(relay.capture(), eq(value), anyInt());
intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
mRelayHandler.handleIntent(intent);
+ mBackgroundExecutor.runAllReady();
verify(mSpyContext).unregisterReceiver(eq(relay.getValue()));
}
@@ -119,6 +143,7 @@
Intent intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
mRelayHandler.handleIntent(intent);
+ mBackgroundExecutor.runAllReady();
// No crash
}
@@ -138,6 +163,7 @@
intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
mRelayHandler.handleIntent(intent);
+ mBackgroundExecutor.runAllReady();
ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mSpyContext).registerReceiver(relay.capture(), eq(value), anyInt());
relay.getValue().onReceive(mSpyContext, new Intent(TEST_ACTION));
@@ -146,8 +172,10 @@
}
@Test
- public void testRegisteredWithDispatcher() {
+ @DisableFlags(Flags.FLAG_SLICE_BROADCAST_RELAY_IN_BACKGROUND)
+ public void testRegisteredWithDispatcher_onMainThread() {
mRelayHandler.start();
+ mBackgroundExecutor.runAllReady();
verify(mBroadcastDispatcher)
.registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
@@ -155,6 +183,19 @@
.registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
}
+ @Test
+ @EnableFlags(Flags.FLAG_SLICE_BROADCAST_RELAY_IN_BACKGROUND)
+ public void testRegisteredWithDispatcher_onBackgroundThread() {
+ mRelayHandler.start();
+ mBackgroundExecutor.runAllReady();
+
+ verify(mBroadcastDispatcher)
+ .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class),
+ eq(mBackgroundExecutor));
+ verify(mSpyContext, never())
+ .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
+ }
+
public static class Receiver extends BroadcastReceiver {
private static BroadcastReceiver sReceiver;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index e076420..44207a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -477,9 +477,8 @@
});
// Verify the method is called in
- // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once and
- // {@link Animator.AnimatorListener#onAnimationEnd} once in {@link ValueAnimator#end()}
- verify(mSpyController, times(2)).updateWindowMagnificationInternal(
+ // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once
+ verify(mSpyController).updateWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
@@ -594,10 +593,10 @@
final float expectedY = (int) (windowBounds.exactCenterY() + expectedOffset
- defaultMagnificationWindowSize / 2);
- // This is called 5 times when (1) first creating WindowlessMirrorWindow (2) SurfaceView is
+ // This is called 4 times when (1) first creating WindowlessMirrorWindow (2) SurfaceView is
// created and we place the mirrored content as a child of the SurfaceView
- // (3) the animation starts (4) the animation updates (5) the animation ends
- verify(mTransaction, times(5))
+ // (3) the animation starts (4) the animation updates
+ verify(mTransaction, times(4))
.setPosition(any(SurfaceControl.class), eq(expectedX), eq(expectedY));
}
@@ -788,9 +787,8 @@
waitForIdleSync();
// Verify the method is called in
- // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once and
- // {@link Animator.AnimatorListener#onAnimationEnd} once in {@link ValueAnimator#end()}
- verify(mSpyController, times(2)).updateWindowMagnificationInternal(
+ // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once
+ verify(mSpyController).updateWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
@@ -832,10 +830,8 @@
deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback2);
// Verify the method is called in
- // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once and
- // {@link Animator.AnimatorListener#onAnimationEnd} once when running the animation at
- // the final duration time.
- verify(mSpyController, times(2)).updateWindowMagnificationInternal(
+ // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once
+ verify(mSpyController).updateWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
index a88654b..01e4d58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
@@ -46,6 +46,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
@@ -459,6 +460,7 @@
final float targetCenterX = sourceBoundsCaptor.getValue().exactCenterX() + 10;
final float targetCenterY = sourceBoundsCaptor.getValue().exactCenterY() + 10;
+ reset(mWindowMagnifierCallback);
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.moveWindowMagnifierToPosition(
targetCenterX, targetCenterY, mAnimationCallback);
@@ -491,6 +493,7 @@
final float centerX = sourceBoundsCaptor.getValue().exactCenterX();
final float centerY = sourceBoundsCaptor.getValue().exactCenterY();
+ reset(mWindowMagnifierCallback);
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.moveWindowMagnifierToPosition(
centerX + 10, centerY + 10, mAnimationCallback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java
new file mode 100644
index 0000000..2f4999b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.ambient.touch;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.EnableFlags;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.Choreographer;
+import android.view.GestureDetector;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.Flags;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+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 org.mockito.MockitoAnnotations;
+
+/**
+ * A test suite for exercising {@link InputSession}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper()
+public class InputSessionTest extends SysuiTestCase {
+ @Mock
+ InputMonitorCompat mInputMonitor;
+
+ @Mock
+ GestureDetector mGestureDetector;
+
+ @Mock
+ InputChannelCompat.InputEventListener mInputEventListener;
+
+ TestableLooper mLooper;
+
+ @Mock
+ Choreographer mChoreographer;
+
+ @Mock
+ InputChannelCompat.InputEventReceiver mInputEventReceiver;
+
+ InputSession mSession;
+
+ InputChannelCompat.InputEventListener mEventListener;
+
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mLooper = TestableLooper.get(this);
+ }
+
+ private void createSession(boolean pilfer) {
+ when(mInputMonitor.getInputReceiver(any(), any(), any()))
+ .thenReturn(mInputEventReceiver);
+ mSession = new InputSession(mInputMonitor, mGestureDetector,
+ mInputEventListener, mChoreographer, mLooper.getLooper(), pilfer);
+ final ArgumentCaptor<InputChannelCompat.InputEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+ verify(mInputMonitor).getInputReceiver(any(), any(), listenerCaptor.capture());
+ mEventListener = listenerCaptor.getValue();
+ }
+
+ /**
+ * Ensures consumed motion events are pilfered when option is set.
+ */
+ @Test
+ public void testPilferOnMotionEventGestureConsume() {
+ createSession(true);
+ final MotionEvent event = Mockito.mock(MotionEvent.class);
+ when(mGestureDetector.onTouchEvent(event)).thenReturn(true);
+ mEventListener.onInputEvent(event);
+ verify(mInputEventListener).onInputEvent(eq(event));
+ verify(mInputMonitor).pilferPointers();
+ }
+
+ /**
+ * Ensures consumed motion events are not pilfered when option is not set.
+ */
+ @Test
+ public void testNoPilferOnMotionEventGestureConsume() {
+ createSession(false);
+ final MotionEvent event = Mockito.mock(MotionEvent.class);
+ when(mGestureDetector.onTouchEvent(event)).thenReturn(true);
+ mEventListener.onInputEvent(event);
+ verify(mInputEventListener).onInputEvent(eq(event));
+ verify(mInputMonitor, never()).pilferPointers();
+ }
+
+ /**
+ * Ensures input events are never pilfered.
+ */
+ @Test
+ public void testNoPilferOnInputEvent() {
+ createSession(true);
+ final InputEvent event = Mockito.mock(InputEvent.class);
+ mEventListener.onInputEvent(event);
+ verify(mInputEventListener).onInputEvent(eq(event));
+ verify(mInputMonitor, never()).pilferPointers();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_DREAM_INPUT_SESSION_PILFER_ONCE)
+ public void testPilferOnce() {
+ createSession(true);
+ final MotionEvent event = Mockito.mock(MotionEvent.class);
+ when(mGestureDetector.onTouchEvent(event)).thenReturn(true);
+ mEventListener.onInputEvent(event);
+ mEventListener.onInputEvent(event);
+ verify(mInputEventListener, times(2)).onInputEvent(eq(event));
+ verify(mInputMonitor, times(1)).pilferPointers();
+ }
+
+ /**
+ * Ensures components are properly disposed.
+ */
+ @Test
+ public void testDispose() {
+ createSession(true);
+ mSession.dispose();
+ verify(mInputMonitor).dispose();
+ verify(mInputEventReceiver).dispose();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
similarity index 81%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
index a127631..1e3b556 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams.touch;
+package com.android.systemui.ambient.touch;
import static com.google.common.truth.Truth.assertThat;
@@ -43,7 +43,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dreams.touch.dagger.InputSessionComponent;
+import com.android.systemui.ambient.touch.dagger.InputSessionComponent;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.display.DisplayHelper;
@@ -67,7 +67,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-public class DreamOverlayTouchMonitorTest extends SysuiTestCase {
+public class TouchMonitorTest extends SysuiTestCase {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -78,7 +78,7 @@
private final InputSession mInputSession;
private final Lifecycle mLifecycle;
private final LifecycleOwner mLifecycleOwner;
- private final DreamOverlayTouchMonitor mMonitor;
+ private final TouchMonitor mMonitor;
private final DefaultLifecycleObserver mLifecycleObserver;
private final InputChannelCompat.InputEventListener mEventListener;
private final GestureDetector.OnGestureListener mGestureListener;
@@ -88,7 +88,7 @@
private final Rect mDisplayBounds = Mockito.mock(Rect.class);
private final IWindowManager mIWindowManager;
- Environment(Set<DreamTouchHandler> handlers) {
+ Environment(Set<TouchHandler> handlers) {
mLifecycle = Mockito.mock(Lifecycle.class);
mLifecycleOwner = Mockito.mock(LifecycleOwner.class);
mIWindowManager = Mockito.mock(IWindowManager.class);
@@ -104,7 +104,7 @@
mDisplayHelper = Mockito.mock(DisplayHelper.class);
when(mDisplayHelper.getMaxBounds(anyInt(), anyInt()))
.thenReturn(mDisplayBounds);
- mMonitor = new DreamOverlayTouchMonitor(mExecutor, mBackgroundExecutor,
+ mMonitor = new TouchMonitor(mExecutor, mBackgroundExecutor,
mLifecycle, mInputFactory, mDisplayHelper, handlers, mIWindowManager, 0);
mMonitor.init();
@@ -157,7 +157,7 @@
@Test
public void testReportedDisplayBounds() {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
final Environment environment = new Environment(Stream.of(touchHandler)
.collect(Collectors.toCollection(HashSet::new)));
@@ -169,8 +169,8 @@
// Verify display bounds passed into TouchHandler#getTouchInitiationRegion
verify(touchHandler).getTouchInitiationRegion(
eq(environment.getDisplayBounds()), any(), any());
- final ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionArgumentCaptor =
- ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+ final ArgumentCaptor<TouchHandler.TouchSession> touchSessionArgumentCaptor =
+ ArgumentCaptor.forClass(TouchHandler.TouchSession.class);
verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture());
// Verify that display bounds provided from TouchSession#getBounds
@@ -180,7 +180,7 @@
@Test
public void testEntryTouchZone() {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
final Rect touchArea = new Rect(4, 4, 8 , 8);
doAnswer(invocation -> {
@@ -208,10 +208,10 @@
@Test
public void testSessionCount() {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
final Rect touchArea = new Rect(4, 4, 8 , 8);
- final DreamTouchHandler unzonedTouchHandler = createTouchHandler();
+ final TouchHandler unzonedTouchHandler = createTouchHandler();
doAnswer(invocation -> {
final Region region = (Region) invocation.getArguments()[1];
region.set(touchArea);
@@ -227,13 +227,13 @@
when(initialEvent.getY()).thenReturn(1.0f);
environment.publishInputEvent(initialEvent);
- ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionCaptor = ArgumentCaptor.forClass(
- DreamTouchHandler.TouchSession.class);
+ ArgumentCaptor<TouchHandler.TouchSession> touchSessionCaptor = ArgumentCaptor.forClass(
+ TouchHandler.TouchSession.class);
// Make sure only one active session.
{
verify(unzonedTouchHandler).onSessionStart(touchSessionCaptor.capture());
- final DreamTouchHandler.TouchSession touchSession = touchSessionCaptor.getValue();
+ final TouchHandler.TouchSession touchSession = touchSessionCaptor.getValue();
assertThat(touchSession.getActiveSessionCount()).isEqualTo(1);
touchSession.pop();
environment.executeAll();
@@ -247,7 +247,7 @@
// Make sure there are two active sessions.
{
verify(touchHandler).onSessionStart(touchSessionCaptor.capture());
- final DreamTouchHandler.TouchSession touchSession = touchSessionCaptor.getValue();
+ final TouchHandler.TouchSession touchSession = touchSessionCaptor.getValue();
assertThat(touchSession.getActiveSessionCount()).isEqualTo(2);
touchSession.pop();
}
@@ -256,7 +256,7 @@
@Test
public void testNoActiveSessionWhenHandlerDisabled() {
- final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+ final TouchHandler touchHandler = Mockito.mock(TouchHandler.class);
// disable the handler
when(touchHandler.isEnabled()).thenReturn(false);
@@ -274,7 +274,7 @@
@Test
public void testInputEventPropagation() {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
final Environment environment = new Environment(Stream.of(touchHandler)
.collect(Collectors.toCollection(HashSet::new)));
@@ -294,7 +294,7 @@
@Test
public void testInputEventPropagationAfterRemoval() {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
final Environment environment = new Environment(Stream.of(touchHandler)
.collect(Collectors.toCollection(HashSet::new)));
@@ -303,7 +303,7 @@
environment.publishInputEvent(initialEvent);
// Ensure session started
- final DreamTouchHandler.TouchSession session = captureSession(touchHandler);
+ final TouchHandler.TouchSession session = captureSession(touchHandler);
final InputChannelCompat.InputEventListener eventListener =
registerInputEventListener(session);
@@ -318,7 +318,7 @@
@Test
public void testInputGesturePropagation() {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
final Environment environment = new Environment(Stream.of(touchHandler)
.collect(Collectors.toCollection(HashSet::new)));
@@ -337,7 +337,7 @@
@Test
public void testGestureConsumption() {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
final Environment environment = new Environment(Stream.of(touchHandler)
.collect(Collectors.toCollection(HashSet::new)));
@@ -360,8 +360,8 @@
@Test
public void testBroadcast() {
- final DreamTouchHandler touchHandler = createTouchHandler();
- final DreamTouchHandler touchHandler2 = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler2 = createTouchHandler();
when(touchHandler2.isEnabled()).thenReturn(true);
final Environment environment = new Environment(Stream.of(touchHandler, touchHandler2)
@@ -386,7 +386,7 @@
@Test
public void testPush() throws InterruptedException, ExecutionException {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
final Environment environment = new Environment(Stream.of(touchHandler)
.collect(Collectors.toCollection(HashSet::new)));
@@ -394,13 +394,13 @@
final InputEvent initialEvent = Mockito.mock(InputEvent.class);
environment.publishInputEvent(initialEvent);
- final DreamTouchHandler.TouchSession session = captureSession(touchHandler);
+ final TouchHandler.TouchSession session = captureSession(touchHandler);
final InputChannelCompat.InputEventListener eventListener =
registerInputEventListener(session);
- final ListenableFuture<DreamTouchHandler.TouchSession> frontSessionFuture = session.push();
+ final ListenableFuture<TouchHandler.TouchSession> frontSessionFuture = session.push();
environment.executeAll();
- final DreamTouchHandler.TouchSession frontSession = frontSessionFuture.get();
+ final TouchHandler.TouchSession frontSession = frontSessionFuture.get();
final InputChannelCompat.InputEventListener frontEventListener =
registerInputEventListener(frontSession);
@@ -412,10 +412,10 @@
Mockito.clearInvocations(eventListener, frontEventListener);
- ListenableFuture<DreamTouchHandler.TouchSession> sessionFuture = frontSession.pop();
+ ListenableFuture<TouchHandler.TouchSession> sessionFuture = frontSession.pop();
environment.executeAll();
- DreamTouchHandler.TouchSession returnedSession = sessionFuture.get();
+ TouchHandler.TouchSession returnedSession = sessionFuture.get();
assertThat(session == returnedSession).isTrue();
environment.executeAll();
@@ -429,10 +429,10 @@
@Test
public void testPop() {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
- final DreamTouchHandler.TouchSession.Callback callback =
- Mockito.mock(DreamTouchHandler.TouchSession.Callback.class);
+ final TouchHandler.TouchSession.Callback callback =
+ Mockito.mock(TouchHandler.TouchSession.Callback.class);
final Environment environment = new Environment(Stream.of(touchHandler)
.collect(Collectors.toCollection(HashSet::new)));
@@ -440,7 +440,7 @@
final InputEvent initialEvent = Mockito.mock(InputEvent.class);
environment.publishInputEvent(initialEvent);
- final DreamTouchHandler.TouchSession session = captureSession(touchHandler);
+ final TouchHandler.TouchSession session = captureSession(touchHandler);
session.registerCallback(callback);
session.pop();
environment.executeAll();
@@ -450,7 +450,7 @@
@Test
public void testPauseWithNoActiveSessions() {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
final Environment environment = new Environment(Stream.of(touchHandler)
.collect(Collectors.toCollection(HashSet::new)));
@@ -464,7 +464,7 @@
@Test
public void testDeferredPauseWithActiveSessions() {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
final Environment environment = new Environment(Stream.of(touchHandler)
.collect(Collectors.toCollection(HashSet::new)));
@@ -481,8 +481,8 @@
environment.publishInputEvent(event);
verify(eventListener).onInputEvent(eq(event));
- final ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionArgumentCaptor =
- ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+ final ArgumentCaptor<TouchHandler.TouchSession> touchSessionArgumentCaptor =
+ ArgumentCaptor.forClass(TouchHandler.TouchSession.class);
verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture());
@@ -502,7 +502,7 @@
@Test
public void testDestroyWithActiveSessions() {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
final Environment environment = new Environment(Stream.of(touchHandler)
.collect(Collectors.toCollection(HashSet::new)));
@@ -519,8 +519,8 @@
environment.publishInputEvent(event);
verify(eventListener).onInputEvent(eq(event));
- final ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionArgumentCaptor =
- ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+ final ArgumentCaptor<TouchHandler.TouchSession> touchSessionArgumentCaptor =
+ ArgumentCaptor.forClass(TouchHandler.TouchSession.class);
verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture());
@@ -535,19 +535,19 @@
@Test
public void testPilfering() {
- final DreamTouchHandler touchHandler1 = createTouchHandler();
- final DreamTouchHandler touchHandler2 = createTouchHandler();
+ final TouchHandler touchHandler1 = createTouchHandler();
+ final TouchHandler touchHandler2 = createTouchHandler();
final Environment environment = new Environment(Stream.of(touchHandler1, touchHandler2)
.collect(Collectors.toCollection(HashSet::new)));
final InputEvent initialEvent = Mockito.mock(InputEvent.class);
environment.publishInputEvent(initialEvent);
- final DreamTouchHandler.TouchSession session1 = captureSession(touchHandler1);
+ final TouchHandler.TouchSession session1 = captureSession(touchHandler1);
final GestureDetector.OnGestureListener gestureListener1 =
registerGestureListener(session1);
- final DreamTouchHandler.TouchSession session2 = captureSession(touchHandler2);
+ final TouchHandler.TouchSession session2 = captureSession(touchHandler2);
final GestureDetector.OnGestureListener gestureListener2 =
registerGestureListener(session2);
when(gestureListener2.onDown(any())).thenReturn(true);
@@ -568,10 +568,10 @@
@Test
public void testOnRemovedCallbackOnStopMonitoring() {
- final DreamTouchHandler touchHandler = createTouchHandler();
+ final TouchHandler touchHandler = createTouchHandler();
- final DreamTouchHandler.TouchSession.Callback callback =
- Mockito.mock(DreamTouchHandler.TouchSession.Callback.class);
+ final TouchHandler.TouchSession.Callback callback =
+ Mockito.mock(TouchHandler.TouchSession.Callback.class);
final Environment environment = new Environment(Stream.of(touchHandler)
.collect(Collectors.toCollection(HashSet::new)));
@@ -579,7 +579,7 @@
final InputEvent initialEvent = Mockito.mock(InputEvent.class);
environment.publishInputEvent(initialEvent);
- final DreamTouchHandler.TouchSession session = captureSession(touchHandler);
+ final TouchHandler.TouchSession session = captureSession(touchHandler);
session.registerCallback(callback);
environment.executeAll();
@@ -593,19 +593,19 @@
verify(callback).onRemoved();
}
- public GestureDetector.OnGestureListener registerGestureListener(DreamTouchHandler handler) {
+ private GestureDetector.OnGestureListener registerGestureListener(TouchHandler handler) {
final GestureDetector.OnGestureListener gestureListener = Mockito.mock(
GestureDetector.OnGestureListener.class);
- final ArgumentCaptor<DreamTouchHandler.TouchSession> sessionCaptor =
- ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+ final ArgumentCaptor<TouchHandler.TouchSession> sessionCaptor =
+ ArgumentCaptor.forClass(TouchHandler.TouchSession.class);
verify(handler).onSessionStart(sessionCaptor.capture());
sessionCaptor.getValue().registerGestureListener(gestureListener);
return gestureListener;
}
- public GestureDetector.OnGestureListener registerGestureListener(
- DreamTouchHandler.TouchSession session) {
+ private GestureDetector.OnGestureListener registerGestureListener(
+ TouchHandler.TouchSession session) {
final GestureDetector.OnGestureListener gestureListener = Mockito.mock(
GestureDetector.OnGestureListener.class);
session.registerGestureListener(gestureListener);
@@ -613,8 +613,8 @@
return gestureListener;
}
- public InputChannelCompat.InputEventListener registerInputEventListener(
- DreamTouchHandler.TouchSession session) {
+ private InputChannelCompat.InputEventListener registerInputEventListener(
+ TouchHandler.TouchSession session) {
final InputChannelCompat.InputEventListener eventListener = Mockito.mock(
InputChannelCompat.InputEventListener.class);
session.registerInputListener(eventListener);
@@ -622,20 +622,20 @@
return eventListener;
}
- public DreamTouchHandler.TouchSession captureSession(DreamTouchHandler handler) {
- final ArgumentCaptor<DreamTouchHandler.TouchSession> sessionCaptor =
- ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+ private TouchHandler.TouchSession captureSession(TouchHandler handler) {
+ final ArgumentCaptor<TouchHandler.TouchSession> sessionCaptor =
+ ArgumentCaptor.forClass(TouchHandler.TouchSession.class);
verify(handler).onSessionStart(sessionCaptor.capture());
return sessionCaptor.getValue();
}
- public InputChannelCompat.InputEventListener registerInputEventListener(
- DreamTouchHandler handler) {
+ private InputChannelCompat.InputEventListener registerInputEventListener(
+ TouchHandler handler) {
return registerInputEventListener(captureSession(handler));
}
- private DreamTouchHandler createTouchHandler() {
- final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+ private TouchHandler createTouchHandler() {
+ final TouchHandler touchHandler = Mockito.mock(TouchHandler.class);
// enable the handler by default
when(touchHandler.isEnabled()).thenReturn(true);
return touchHandler;
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 f490f3c..cbad133 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
@@ -41,7 +41,6 @@
import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.QuickSettingsController
import com.android.systemui.shade.ShadeController
@@ -109,7 +108,6 @@
headsUpManager,
powerInteractor,
activeNotificationsInteractor,
- kosmos.sceneContainerFlags,
kosmos::sceneInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 33a6010..8f3fed7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.biometrics
+import android.app.ActivityTaskManager
import android.app.admin.DevicePolicyManager
import android.content.pm.PackageManager
import android.hardware.biometrics.BiometricAuthenticator
@@ -44,9 +45,12 @@
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.FakePromptRepository
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
@@ -120,10 +124,12 @@
lateinit var selectedUserInteractor: SelectedUserInteractor
@Mock
private lateinit var packageManager: PackageManager
+ @Mock private lateinit var activityTaskManager: ActivityTaskManager
private val testScope = TestScope(StandardTestDispatcher())
private val fakeExecutor = FakeExecutor(FakeSystemClock())
private val biometricPromptRepository = FakePromptRepository()
+ private val biometricStatusRepository = FakeBiometricStatusRepository()
private val fingerprintRepository = FakeFingerprintPropertyRepository()
private val displayStateRepository = FakeDisplayStateRepository()
private val credentialInteractor = FakeCredentialInteractor()
@@ -143,6 +149,7 @@
private lateinit var displayRepository: FakeDisplayRepository
private lateinit var displayStateInteractor: DisplayStateInteractor
private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
+ private lateinit var biometricStatusInteractor: BiometricStatusInteractor
private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
@@ -168,6 +175,8 @@
selectedUserInteractor,
testScope.backgroundScope,
)
+ biometricStatusInteractor =
+ BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository)
// Set up default logo icon
whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon)
context.setMockPackageManager(packageManager)
@@ -645,6 +654,7 @@
promptSelectorInteractor,
context,
udfpsOverlayInteractor,
+ biometricStatusInteractor,
udfpsUtils
),
{ credentialViewModel },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
index 99c2c40..aff93bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
@@ -465,10 +465,34 @@
nativeYOutsideSensor = 150f,
)
-/* ROTATION_180 is not supported. It's treated the same as ROTATION_0. */
+/*
+ * ROTATION_180 map:
+ * _ _ _ _
+ * _ _ s _
+ * _ _ s _
+ * _ _ _ _
+ * _ O _ _
+ * _ _ _ _
+ *
+ * (_) empty space
+ * (S) sensor
+ * (O) touch outside of the sensor
+ */
+private val ROTATION_180_NATIVE_SENSOR_BOUNDS =
+ Rect(
+ 200, /* left */
+ 100, /* top */
+ 300, /* right */
+ 300, /* bottom */
+ )
private val ROTATION_180_INPUTS =
- ROTATION_0_INPUTS.copy(
+ OrientationBasedInputs(
rotation = Surface.ROTATION_180,
+ nativeOrientation = (ORIENTATION - Math.PI.toFloat() / 2),
+ nativeXWithinSensor = ROTATION_180_NATIVE_SENSOR_BOUNDS.exactCenterX(),
+ nativeYWithinSensor = ROTATION_180_NATIVE_SENSOR_BOUNDS.exactCenterY(),
+ nativeXOutsideSensor = 150f,
+ nativeYOutsideSensor = 450f,
)
/*
@@ -639,33 +663,6 @@
}
}
-private fun genTestCasesForUnsupportedAction(
- motionEventAction: Int
-): List<SinglePointerTouchProcessorTest.TestCase> {
- val isGoodOverlap = true
- val previousPointerOnSensorIds = listOf(INVALID_POINTER_ID, POINTER_ID_1)
- return previousPointerOnSensorIds.map { previousPointerOnSensorId ->
- val overlayParams = ROTATION_0_INPUTS.toOverlayParams(scaleFactor = 1f)
- val nativeX = ROTATION_0_INPUTS.getNativeX(isGoodOverlap)
- val nativeY = ROTATION_0_INPUTS.getNativeY(isGoodOverlap)
- val event =
- MOTION_EVENT.copy(
- action = motionEventAction,
- x = nativeX,
- y = nativeY,
- minor = NATIVE_MINOR,
- major = NATIVE_MAJOR,
- )
- SinglePointerTouchProcessorTest.TestCase(
- event = event,
- currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = isGoodOverlap)),
- previousPointerOnSensorId = previousPointerOnSensorId,
- overlayParams = overlayParams,
- expected = TouchProcessorResult.Failure(),
- )
- }
-}
-
private fun obtainMotionEvent(
action: Int,
pointerId: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index 30c5e6e..d3cc232 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -78,7 +78,6 @@
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
import com.android.systemui.statusbar.phone.dozeServiceHost
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -239,7 +238,6 @@
testScope.backgroundScope,
mContext,
deviceEntryFingerprintAuthRepository,
- kosmos.fakeSceneContainerFlags,
kosmos.sceneInteractor,
primaryBouncerInteractor,
alternateBouncerInteractor,
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 5b0df5d..a6c7f72 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
@@ -16,12 +16,14 @@
package com.android.systemui.biometrics.ui.viewmodel
+import android.app.ActivityTaskManager
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.drawable.BitmapDrawable
+import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
import android.hardware.biometrics.PromptContentItemBulletedText
import android.hardware.biometrics.PromptContentView
@@ -40,9 +42,12 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.UdfpsUtils
+import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.FakePromptRepository
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
@@ -51,6 +56,7 @@
import com.android.systemui.biometrics.extractAuthenticatorTypes
import com.android.systemui.biometrics.faceSensorPropertiesInternal
import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
import com.android.systemui.biometrics.shared.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.DisplayRotation
@@ -59,6 +65,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.res.R
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
@@ -102,6 +109,7 @@
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var applicationInfoWithIcon: ApplicationInfo
@Mock private lateinit var applicationInfoNoIcon: ApplicationInfo
+ @Mock private lateinit var activityTaskManager: ActivityTaskManager
private val fakeExecutor = FakeExecutor(FakeSystemClock())
private val testScope = TestScope()
@@ -115,9 +123,11 @@
private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
private lateinit var promptRepository: FakePromptRepository
private lateinit var displayStateRepository: FakeDisplayStateRepository
+ private lateinit var biometricStatusRepository: FakeBiometricStatusRepository
private lateinit var displayRepository: FakeDisplayRepository
private lateinit var displayStateInteractor: DisplayStateInteractor
private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
+ private lateinit var biometricStatusInteractor: BiometricStatusInteractor
private lateinit var selector: PromptSelectorInteractor
private lateinit var viewModel: PromptViewModel
@@ -157,6 +167,9 @@
selectedUserInteractor,
testScope.backgroundScope
)
+ biometricStatusRepository = FakeBiometricStatusRepository()
+ biometricStatusInteractor =
+ BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository)
selector =
PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
selector.resetPrompt()
@@ -178,6 +191,7 @@
selector,
mContext,
udfpsOverlayInteractor,
+ biometricStatusInteractor,
udfpsUtils
)
iconViewModel = viewModel.iconViewModel
@@ -1053,8 +1067,7 @@
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) {
+ if (testCase.isCoex) {
viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
}
viewModel.showAuthenticated(testCase.authenticatedModality, 0)
@@ -1069,14 +1082,18 @@
assertThat(canTryAgain).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
- if (testCase.isFaceOnly && expectConfirmation) {
- assertThat(size).isEqualTo(PromptSize.MEDIUM)
- assertButtonsVisible(
- cancel = true,
- confirm = true,
- )
+ if (expectConfirmation) {
+ if (testCase.isFaceOnly) {
+ assertThat(size).isEqualTo(PromptSize.MEDIUM)
+ assertButtonsVisible(
+ cancel = true,
+ confirm = true,
+ )
- viewModel.confirmAuthenticated()
+ viewModel.confirmAuthenticated()
+ } else if (testCase.isCoex) {
+ assertThat(authenticated?.isAuthenticatedAndConfirmed).isTrue()
+ }
assertThat(message).isEqualTo(PromptMessage.Empty)
assertButtonsVisible()
}
@@ -1086,8 +1103,7 @@
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) {
+ if (testCase.isCoex) {
viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_UP))
}
@@ -1413,6 +1429,12 @@
packageName = packageName,
)
+ biometricStatusRepository.setFingerprintAcquiredStatus(
+ AcquiredFingerprintAuthenticationStatus(
+ AuthenticationReason.BiometricPromptAuthentication,
+ BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN
+ )
+ )
// put the view model in the initial authenticating state, unless explicitly skipped
val startMode =
when {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
index 238a76e..415da02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -77,7 +77,6 @@
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
import com.android.systemui.statusbar.phone.dozeServiceHost
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -236,7 +235,6 @@
testScope.backgroundScope,
mContext,
deviceEntryFingerprintAuthRepository,
- kosmos.fakeSceneContainerFlags,
kosmos.sceneInteractor,
primaryBouncerInteractor,
alternateBouncerInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
new file mode 100644
index 0000000..8a1a082
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.res.R
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class AudioSharingInteractorTest : SysuiTestCase() {
+ private val testDispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+ private val bluetoothState = MutableStateFlow(false)
+ private val deviceItemUpdate: MutableSharedFlow<List<DeviceItem>> = MutableSharedFlow()
+ @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice
+ @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
+ @Mock private lateinit var bluetoothStateInteractor: BluetoothStateInteractor
+ @Mock private lateinit var deviceItemInteractor: DeviceItemInteractor
+ @Mock private lateinit var deviceItem: DeviceItem
+ private lateinit var mockitoSession: StaticMockitoSession
+ private lateinit var audioSharingInteractor: AudioSharingInteractor
+
+ @Before
+ fun setUp() {
+ mockitoSession =
+ mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking()
+ whenever(bluetoothStateInteractor.bluetoothStateUpdate).thenReturn(bluetoothState)
+ whenever(deviceItemInteractor.deviceItemUpdate).thenReturn(deviceItemUpdate)
+ audioSharingInteractor =
+ AudioSharingInteractor(
+ localBluetoothManager,
+ bluetoothStateInteractor,
+ deviceItemInteractor,
+ testScope.backgroundScope,
+ testDispatcher,
+ )
+ }
+
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
+ }
+
+ @Test
+ fun testButtonStateUpdate_bluetoothOff_returnGone() {
+ testScope.runTest {
+ val actual by collectLastValue(audioSharingInteractor.audioSharingButtonStateUpdate)
+
+ assertThat(actual).isEqualTo(AudioSharingButtonState.Gone)
+ }
+ }
+
+ @Test
+ fun testButtonStateUpdate_noDevice_returnGone() {
+ testScope.runTest {
+ val actual by collectLastValue(audioSharingInteractor.audioSharingButtonStateUpdate)
+ bluetoothState.value = true
+ runCurrent()
+
+ assertThat(actual).isEqualTo(AudioSharingButtonState.Gone)
+ }
+ }
+
+ @Test
+ fun testButtonStateUpdate_isBroadcasting_returnSharingAudio() {
+ testScope.runTest {
+ whenever(BluetoothUtils.isBroadcasting(localBluetoothManager)).thenReturn(true)
+
+ val actual by collectLastValue(audioSharingInteractor.audioSharingButtonStateUpdate)
+ bluetoothState.value = true
+ deviceItemUpdate.emit(listOf())
+ runCurrent()
+
+ assertThat(actual)
+ .isEqualTo(
+ AudioSharingButtonState.Visible(
+ R.string.quick_settings_bluetooth_audio_sharing_button_sharing
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testButtonStateUpdate_hasSource_returnGone() {
+ testScope.runTest {
+ whenever(BluetoothUtils.isBroadcasting(localBluetoothManager)).thenReturn(false)
+ whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
+ whenever(
+ BluetoothUtils.hasConnectedBroadcastSource(
+ cachedBluetoothDevice,
+ localBluetoothManager
+ )
+ )
+ .thenReturn(true)
+
+ val actual by collectLastValue(audioSharingInteractor.audioSharingButtonStateUpdate)
+ bluetoothState.value = true
+ deviceItemUpdate.emit(listOf(deviceItem))
+ runCurrent()
+
+ assertThat(actual).isEqualTo(AudioSharingButtonState.Gone)
+ }
+ }
+
+ @Test
+ fun testButtonStateUpdate_hasActiveDevice_returnAudioSharing() {
+ testScope.runTest {
+ whenever(BluetoothUtils.isBroadcasting(localBluetoothManager)).thenReturn(false)
+ whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
+ whenever(
+ BluetoothUtils.hasConnectedBroadcastSource(
+ cachedBluetoothDevice,
+ localBluetoothManager
+ )
+ )
+ .thenReturn(false)
+ whenever(BluetoothUtils.isActiveLeAudioDevice(cachedBluetoothDevice)).thenReturn(true)
+
+ val actual by collectLastValue(audioSharingInteractor.audioSharingButtonStateUpdate)
+ bluetoothState.value = true
+ deviceItemUpdate.emit(listOf(deviceItem))
+ runCurrent()
+
+ assertThat(actual)
+ .isEqualTo(
+ AudioSharingButtonState.Visible(
+ R.string.quick_settings_bluetooth_audio_sharing_button
+ )
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
index a8f82ed..6fe7d86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
@@ -23,6 +23,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -41,7 +42,8 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class BluetoothStateInteractorTest : SysuiTestCase() {
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
- private val testScope = TestScope()
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
private lateinit var bluetoothStateInteractor: BluetoothStateInteractor
@@ -52,7 +54,12 @@
@Before
fun setUp() {
bluetoothStateInteractor =
- BluetoothStateInteractor(localBluetoothManager, logger, testScope.backgroundScope)
+ BluetoothStateInteractor(
+ localBluetoothManager,
+ logger,
+ testScope.backgroundScope,
+ testDispatcher
+ )
`when`(localBluetoothManager.bluetoothAdapter).thenReturn(bluetoothAdapter)
}
@@ -61,7 +68,7 @@
testScope.runTest {
`when`(bluetoothAdapter.isEnabled).thenReturn(true)
- assertThat(bluetoothStateInteractor.isBluetoothEnabled).isTrue()
+ assertThat(bluetoothStateInteractor.isBluetoothEnabled()).isTrue()
}
}
@@ -70,7 +77,7 @@
testScope.runTest {
`when`(bluetoothAdapter.isEnabled).thenReturn(false)
- assertThat(bluetoothStateInteractor.isBluetoothEnabled).isFalse()
+ assertThat(bluetoothStateInteractor.isBluetoothEnabled()).isFalse()
}
}
@@ -79,7 +86,7 @@
testScope.runTest {
`when`(bluetoothAdapter.isEnabled).thenReturn(false)
- bluetoothStateInteractor.isBluetoothEnabled = true
+ bluetoothStateInteractor.setBluetoothEnabled(true)
verify(bluetoothAdapter).enable()
verify(logger)
.logBluetoothState(BluetoothStateStage.BLUETOOTH_STATE_VALUE_SET, true.toString())
@@ -91,7 +98,7 @@
testScope.runTest {
`when`(bluetoothAdapter.isEnabled).thenReturn(false)
- bluetoothStateInteractor.isBluetoothEnabled = false
+ bluetoothStateInteractor.setBluetoothEnabled(false)
verify(bluetoothAdapter, never()).enable()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
index 12dfe97..62c98b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
@@ -110,7 +110,6 @@
BluetoothTileDialogDelegate(
uiProperties,
CONTENT_HEIGHT,
- ENABLED,
bluetoothTileDialogCallback,
{},
dispatcher,
@@ -211,7 +210,6 @@
BluetoothTileDialogDelegate(
uiProperties,
CONTENT_HEIGHT,
- ENABLED,
bluetoothTileDialogCallback,
{},
dispatcher,
@@ -267,7 +265,6 @@
BluetoothTileDialogDelegate(
BluetoothTileDialogViewModel.UiProperties.build(ENABLED, ENABLED),
cachedHeight,
- ENABLED,
bluetoothTileDialogCallback,
{},
dispatcher,
@@ -291,7 +288,6 @@
BluetoothTileDialogDelegate(
BluetoothTileDialogViewModel.UiProperties.build(ENABLED, ENABLED),
MATCH_PARENT,
- ENABLED,
bluetoothTileDialogCallback,
{},
dispatcher,
@@ -315,7 +311,6 @@
BluetoothTileDialogDelegate(
BluetoothTileDialogViewModel.UiProperties.build(ENABLED, ENABLED),
MATCH_PARENT,
- ENABLED,
bluetoothTileDialogCallback,
{},
dispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
index 6d99c5b..b05d959 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
@@ -52,7 +52,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
@@ -74,7 +73,7 @@
@Mock private lateinit var bluetoothStateInteractor: BluetoothStateInteractor
- @Mock private lateinit var bluetoothAutoOnInteractor: BluetoothAutoOnInteractor
+ @Mock private lateinit var audioSharingInteractor: AudioSharingInteractor
@Mock private lateinit var deviceItemInteractor: DeviceItemInteractor
@@ -92,6 +91,8 @@
@Mock private lateinit var localBluetoothManager: LocalBluetoothManager
+ @Mock private lateinit var bluetoothTileDialogLogger: BluetoothTileDialogLogger
+
@Mock
private lateinit var mBluetoothTileDialogDelegateDelegateFactory:
BluetoothTileDialogDelegate.Factory
@@ -115,7 +116,12 @@
bluetoothTileDialogViewModel =
BluetoothTileDialogViewModel(
deviceItemInteractor,
- bluetoothStateInteractor,
+ BluetoothStateInteractor(
+ localBluetoothManager,
+ bluetoothTileDialogLogger,
+ testScope.backgroundScope,
+ dispatcher
+ ),
// TODO(b/316822488): Create FakeBluetoothAutoOnInteractor.
BluetoothAutoOnInteractor(
BluetoothAutoOnRepository(
@@ -125,6 +131,7 @@
dispatcher
)
),
+ audioSharingInteractor,
mDialogTransitionAnimator,
activityStarter,
uiEventLogger,
@@ -135,20 +142,9 @@
mBluetoothTileDialogDelegateDelegateFactory
)
whenever(deviceItemInteractor.deviceItemUpdate).thenReturn(MutableSharedFlow())
- whenever(bluetoothStateInteractor.bluetoothStateUpdate)
- .thenReturn(MutableStateFlow(null).asStateFlow())
whenever(deviceItemInteractor.deviceItemUpdateRequest)
.thenReturn(MutableStateFlow(Unit).asStateFlow())
- whenever(bluetoothStateInteractor.isBluetoothEnabled).thenReturn(true)
- whenever(
- mBluetoothTileDialogDelegateDelegateFactory.create(
- any(),
- anyInt(),
- ArgumentMatchers.anyBoolean(),
- any(),
- any()
- )
- )
+ whenever(mBluetoothTileDialogDelegateDelegateFactory.create(any(), anyInt(), any(), any()))
.thenReturn(bluetoothTileDialogDelegate)
whenever(bluetoothTileDialogDelegate.createDialog()).thenReturn(sysuiDialog)
whenever(sysuiDialog.context).thenReturn(mContext)
@@ -159,6 +155,8 @@
whenever(bluetoothTileDialogDelegate.contentHeight).thenReturn(getMutableStateFlow(0))
whenever(bluetoothTileDialogDelegate.bluetoothAutoOnToggle)
.thenReturn(getMutableStateFlow(false))
+ whenever(audioSharingInteractor.audioSharingButtonStateUpdate)
+ .thenReturn(getMutableStateFlow(AudioSharingButtonState.Gone))
}
@Test
@@ -201,15 +199,6 @@
}
@Test
- fun testShowDialog_withBluetoothStateValue() {
- testScope.runTest {
- bluetoothTileDialogViewModel.showDialog(null)
-
- verify(bluetoothStateInteractor).bluetoothStateUpdate
- }
- }
-
- @Test
fun testStartSettingsActivity_activityLaunched_dialogDismissed() {
testScope.runTest {
whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
index eb735cb..daf4a3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 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.
@@ -281,7 +281,7 @@
override fun isFilterMatched(
context: Context,
cachedDevice: CachedBluetoothDevice,
- audioManager: AudioManager?
+ audioManager: AudioManager
) = isFilterMatchFunc(cachedDevice)
override fun create(context: Context, cachedDevice: CachedBluetoothDevice) = deviceItem
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
index dac88a3..e06134b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
@@ -119,6 +119,7 @@
fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromOffState() =
testScope.runTest {
underTest.start()
+ runCurrent()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_LID)
faceWakeUpTriggersConfig.setTriggerFaceAuthOnWakeUpFrom(
@@ -160,6 +161,7 @@
fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromAodState() =
testScope.runTest {
underTest.start()
+ runCurrent()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_LID)
faceWakeUpTriggersConfig.setTriggerFaceAuthOnWakeUpFrom(
@@ -207,6 +209,7 @@
fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromDozingState() =
testScope.runTest {
underTest.start()
+ runCurrent()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_LID)
faceWakeUpTriggersConfig.setTriggerFaceAuthOnWakeUpFrom(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt
new file mode 100644
index 0000000..d118cc7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt
@@ -0,0 +1,167 @@
+/*
+ * 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.graphics.Insets
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+import android.view.View
+import android.view.Window
+import android.view.WindowInsets
+import android.view.WindowInsetsAnimation
+import androidx.test.filters.SmallTest
+import com.android.app.animation.Interpolators
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class MirroringConfirmationDialogDelegateTest : SysuiTestCase() {
+
+ private lateinit var underTest: MirroringConfirmationDialogDelegate
+
+ private val onStartMirroringCallback = mock<View.OnClickListener>()
+ private val onCancelCallback = mock<View.OnClickListener>()
+ private val windowDecorView: View = mock {}
+ private val windowInsetsAnimationCallbackCaptor =
+ ArgumentCaptor.forClass(WindowInsetsAnimation.Callback::class.java)
+ private val dialog: Dialog =
+ mock<Dialog> {
+ var view: View? = null
+ whenever(setContentView(any<Int>())).then {
+ view =
+ LayoutInflater.from(this@MirroringConfirmationDialogDelegateTest.context)
+ .inflate(it.arguments[0] as Int, null, false)
+ Unit
+ }
+ whenever(requireViewById<View>(any<Int>())).then {
+ view?.requireViewById(it.arguments[0] as Int)
+ }
+ val window: Window = mock { whenever(decorView).thenReturn(windowDecorView) }
+ whenever(this.window).thenReturn(window)
+ }
+
+ @Before
+ fun setUp() {
+ underTest =
+ MirroringConfirmationDialogDelegate(
+ context = context,
+ showConcurrentDisplayInfo = false,
+ onStartMirroringClickListener = onStartMirroringCallback,
+ onCancelMirroring = onCancelCallback,
+ navbarBottomInsetsProvider = { 0 },
+ )
+ }
+
+ @Test
+ fun startMirroringButton_clicked_callsCorrectCallback() {
+ underTest.onCreate(dialog, null)
+
+ dialog.requireViewById<View>(R.id.enable_display).callOnClick()
+
+ verify(onStartMirroringCallback).onClick(any())
+ verify(onCancelCallback, never()).onClick(any())
+ }
+
+ @Test
+ fun cancelButton_clicked_callsCorrectCallback() {
+ underTest.onCreate(dialog, null)
+
+ dialog.requireViewById<View>(R.id.cancel).callOnClick()
+
+ verify(onCancelCallback).onClick(any())
+ verify(onStartMirroringCallback, never()).onClick(any())
+ }
+
+ @Test
+ fun onCancel_afterEnablingMirroring_cancelCallbackNotCalled() {
+ underTest.onCreate(dialog, null)
+ dialog.requireViewById<View>(R.id.enable_display).callOnClick()
+
+ underTest.onStop(dialog)
+
+ verify(onCancelCallback, never()).onClick(any())
+ verify(onStartMirroringCallback).onClick(any())
+ }
+
+ @Test
+ fun onDismiss_afterEnablingMirroring_cancelCallbackNotCalled() {
+ underTest.onCreate(dialog, null)
+ dialog.requireViewById<View>(R.id.enable_display).callOnClick()
+
+ underTest.onStop(dialog)
+
+ verify(onCancelCallback, never()).onClick(any())
+ verify(onStartMirroringCallback).onClick(any())
+ }
+
+ @Test
+ fun onInsetsChanged_navBarInsets_updatesBottomPadding() {
+ underTest.onCreate(dialog, null)
+ underTest.onStart(dialog)
+
+ val insets = buildInsets(WindowInsets.Type.navigationBars(), TEST_BOTTOM_INSETS)
+
+ triggerInsetsChanged(WindowInsets.Type.navigationBars(), insets)
+
+ assertThat(dialog.requireViewById<View>(R.id.cd_bottom_sheet).paddingBottom)
+ .isEqualTo(TEST_BOTTOM_INSETS)
+ }
+
+ @Test
+ fun onInsetsChanged_otherType_doesNotUpdateBottomPadding() {
+ underTest.onCreate(dialog, null)
+ underTest.onStart(dialog)
+
+ val insets = buildInsets(WindowInsets.Type.ime(), TEST_BOTTOM_INSETS)
+ triggerInsetsChanged(WindowInsets.Type.ime(), insets)
+
+ assertThat(dialog.requireViewById<View>(R.id.cd_bottom_sheet).paddingBottom)
+ .isNotEqualTo(TEST_BOTTOM_INSETS)
+ }
+
+ private fun buildInsets(@WindowInsets.Type.InsetsType type: Int, bottom: Int): WindowInsets {
+ return WindowInsets.Builder().setInsets(type, Insets.of(0, 0, 0, bottom)).build()
+ }
+
+ private fun triggerInsetsChanged(type: Int, insets: WindowInsets) {
+ verify(windowDecorView)
+ .setWindowInsetsAnimationCallback(capture(windowInsetsAnimationCallbackCaptor))
+ windowInsetsAnimationCallbackCaptor.value.onProgress(
+ insets,
+ listOf(WindowInsetsAnimation(type, Interpolators.INSTANT, 0))
+ )
+ }
+
+ private companion object {
+ const val TEST_BOTTOM_INSETS = 1000 // arbitrarily high number
+ }
+}
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
deleted file mode 100644
index 30519b0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.display.ui.view
-
-import android.graphics.Insets
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.View
-import android.view.WindowInsets
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.res.R
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.google.common.truth.Truth.assertThat
-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,
- navbarBottomInsetsProvider = { 0 },
- )
- }
-
- @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())
- }
-
- @Test
- fun onCancel_afterEnablingMirroring_cancelCallbackNotCalled() {
- dialog.show()
- dialog.requireViewById<View>(R.id.enable_display).callOnClick()
-
- dialog.cancel()
-
- verify(onCancelCallback, never()).onClick(any())
- verify(onStartMirroringCallback).onClick(any())
- }
-
- @Test
- fun onDismiss_afterEnablingMirroring_cancelCallbackNotCalled() {
- dialog.show()
- dialog.requireViewById<View>(R.id.enable_display).callOnClick()
-
- dialog.dismiss()
-
- verify(onCancelCallback, never()).onClick(any())
- verify(onStartMirroringCallback).onClick(any())
- }
-
- @Test
- fun onInsetsChanged_navBarInsets_updatesBottomPadding() {
- dialog.show()
-
- val insets = buildInsets(WindowInsets.Type.navigationBars(), TEST_BOTTOM_INSETS)
- dialog.onInsetsChanged(WindowInsets.Type.navigationBars(), insets)
-
- assertThat(dialog.requireViewById<View>(R.id.cd_bottom_sheet).paddingBottom)
- .isEqualTo(TEST_BOTTOM_INSETS)
- }
-
- @Test
- fun onInsetsChanged_otherType_doesNotUpdateBottomPadding() {
- dialog.show()
-
- val insets = buildInsets(WindowInsets.Type.ime(), TEST_BOTTOM_INSETS)
- dialog.onInsetsChanged(WindowInsets.Type.ime(), insets)
-
- assertThat(dialog.requireViewById<View>(R.id.cd_bottom_sheet).paddingBottom)
- .isNotEqualTo(TEST_BOTTOM_INSETS)
- }
-
- private fun buildInsets(@WindowInsets.Type.InsetsType type: Int, bottom: Int): WindowInsets {
- return WindowInsets.Builder().setInsets(type, Insets.of(0, 0, 0, bottom)).build()
- }
-
- @After
- fun teardown() {
- if (::dialog.isInitialized) {
- dialog.dismiss()
- }
- }
-
- private companion object {
- const val TEST_BOTTOM_INSETS = 1000 // arbitrarily high number
- }
-}
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 11ec417f..709f779 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -25,8 +25,8 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
-import static com.android.systemui.Flags.FLAG_REFACTOR_GET_CURRENT_USER;
import static com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR;
+import static com.android.systemui.Flags.FLAG_REFACTOR_GET_CURRENT_USER;
import static com.android.systemui.keyguard.KeyguardViewMediator.DELAYED_KEYGUARD_ACTION;
import static com.android.systemui.keyguard.KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT;
import static com.android.systemui.keyguard.KeyguardViewMediator.REBOOT_MAINLINE_UPDATE;
@@ -91,6 +91,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.ui.viewmodel.DreamViewModel;
import com.android.systemui.dump.DumpManager;
@@ -101,7 +102,6 @@
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.scene.FakeWindowRootViewComponent;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
@@ -219,8 +219,8 @@
private @Mock CoroutineDispatcher mDispatcher;
private @Mock DreamViewModel mDreamViewModel;
+ private @Mock CommunalTransitionViewModel mCommunalTransitionViewModel;
private @Mock SystemPropertiesHelper mSystemPropertiesHelper;
- private @Mock SceneContainerFlags mSceneContainerFlags;
private FakeFeatureFlags mFeatureFlags;
private final int mDefaultUserId = 100;
@@ -241,6 +241,10 @@
.thenReturn(mock(Flow.class));
when(mDreamViewModel.getTransitionEnded())
.thenReturn(mock(Flow.class));
+ when(mCommunalTransitionViewModel.getShowByDefault())
+ .thenReturn(mock(Flow.class));
+ when(mCommunalTransitionViewModel.getTransitionFromOccludedEnded())
+ .thenReturn(mock(Flow.class));
when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(mDefaultUserId);
when(mSelectedUserInteractor.getSelectedUserId(anyBoolean())).thenReturn(mDefaultUserId);
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
@@ -264,7 +268,6 @@
mShadeWindowLogger,
() -> mSelectedUserInteractor,
mUserTracker,
- mSceneContainerFlags,
mKosmos::getCommunalInteractor);
mFeatureFlags = new FakeFeatureFlags();
mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER);
@@ -1229,6 +1232,7 @@
mSystemClock,
mDispatcher,
() -> mDreamViewModel,
+ () -> mCommunalTransitionViewModel,
mSystemPropertiesHelper,
() -> mock(WindowManagerLockscreenVisibilityManager.class),
mSelectedUserInteractor,
@@ -1236,7 +1240,7 @@
mock(WindowManagerOcclusionManager.class));
mViewMediator.start();
- mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);
+ mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null);
}
private void captureKeyguardStateControllerCallback() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index 2b51863..b50d248 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -15,14 +15,14 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.utils.GlobalWindowManager
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -42,8 +42,7 @@
class ResourceTrimmerTest : SysuiTestCase() {
val kosmos = testKosmos()
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ private val testScope = kosmos.testScope
private val keyguardRepository = kosmos.fakeKeyguardRepository
private val featureFlags = kosmos.fakeFeatureFlagsClassic
private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
@@ -63,8 +62,8 @@
val withDeps =
KeyguardInteractorFactory.create(
- repository = keyguardRepository,
featureFlags = featureFlags,
+ repository = keyguardRepository,
)
val keyguardInteractor = withDeps.keyguardInteractor
resourceTrimmer =
@@ -74,7 +73,7 @@
kosmos.keyguardTransitionInteractor,
globalWindowManager,
testScope.backgroundScope,
- testDispatcher,
+ kosmos.testDispatcher,
featureFlags
)
resourceTrimmer.start()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
index d75cbec..d52e911 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
@@ -21,7 +21,10 @@
import com.android.keyguard.ClockEventController
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.SettingsClockSize
+import com.android.systemui.res.R
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth
@@ -49,6 +52,7 @@
private lateinit var fakeSettings: FakeSettings
@Mock private lateinit var clockRegistry: ClockRegistry
@Mock private lateinit var clockEventController: ClockEventController
+ private val fakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
@Before
fun setup() {
@@ -63,7 +67,9 @@
clockRegistry,
clockEventController,
dispatcher,
- scope.backgroundScope
+ scope.backgroundScope,
+ context,
+ fakeFeatureFlagsClassic,
)
}
@@ -82,4 +88,12 @@
val value = collectLastValue(underTest.selectedClockSize)
Truth.assertThat(value()).isEqualTo(SettingsClockSize.DYNAMIC)
}
+
+ @Test
+ fun testShouldForceSmallClock() =
+ scope.runTest {
+ overrideResource(R.bool.force_small_clock_on_lockscreen, true)
+ fakeFeatureFlagsClassic.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, true)
+ Truth.assertThat(underTest.shouldForceSmallClock).isTrue()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index 9266af4..dc7f372 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -32,6 +32,7 @@
import com.android.systemui.coroutines.collectValues
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
@@ -40,7 +41,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -87,7 +87,6 @@
@Before
fun setup() {
mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
- kosmos.fakeSceneContainerFlags.enabled = false
primaryBouncerInteractor =
PrimaryBouncerInteractor(
@@ -127,7 +126,6 @@
testScope.backgroundScope,
mContext,
deviceEntryFingerprintAuthRepository,
- kosmos.fakeSceneContainerFlags,
kosmos.sceneInteractor,
primaryBouncerInteractor,
alternateBouncerInteractor,
@@ -168,15 +166,14 @@
}
@Test
+ @EnableSceneContainer
fun updatesShowIndicatorForDeviceEntry_onBouncerSceneActive() =
testScope.runTest {
- kosmos.fakeSceneContainerFlags.enabled = true
underTest =
DeviceEntrySideFpsOverlayInteractor(
testScope.backgroundScope,
mContext,
deviceEntryFingerprintAuthRepository,
- kosmos.fakeSceneContainerFlags,
kosmos.sceneInteractor,
primaryBouncerInteractor,
alternateBouncerInteractor,
@@ -196,15 +193,14 @@
}
@Test
+ @EnableSceneContainer
fun updatesShowIndicatorForDeviceEntry_onBouncerSceneInactive() =
testScope.runTest {
- kosmos.fakeSceneContainerFlags.enabled = true
underTest =
DeviceEntrySideFpsOverlayInteractor(
testScope.backgroundScope,
mContext,
deviceEntryFingerprintAuthRepository,
- kosmos.fakeSceneContainerFlags,
kosmos.sceneInteractor,
primaryBouncerInteractor,
alternateBouncerInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
index b6b4571..4270236 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -27,16 +27,12 @@
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.WEATHER_CLOCK_BLUEPRINT_ID
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.clocks.ClockConfig
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.res.R
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -103,72 +99,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
- fun composeLockscreenOff_DoesAppliesSplitShadeWeatherClockBlueprint() {
- testScope.runTest {
- val blueprint by collectLastValue(underTest.blueprint)
- whenever(clockController.config)
- .thenReturn(
- ClockConfig(
- id = "DIGITAL_CLOCK_WEATHER",
- name = "clock",
- description = "clock",
- )
- )
- clockRepository.setCurrentClock(clockController)
- overrideResource(R.bool.config_use_split_notification_shade, true)
- configurationRepository.onConfigurationChange()
- runCurrent()
-
- assertThat(blueprint?.id).isNotEqualTo(SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID)
- }
- }
-
- @Test
- @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
- fun testDoesAppliesSplitShadeWeatherClockBlueprint() {
- testScope.runTest {
- val blueprint by collectLastValue(underTest.blueprint)
- whenever(clockController.config)
- .thenReturn(
- ClockConfig(
- id = "DIGITAL_CLOCK_WEATHER",
- name = "clock",
- description = "clock",
- )
- )
- clockRepository.setCurrentClock(clockController)
- overrideResource(R.bool.config_use_split_notification_shade, true)
- configurationRepository.onConfigurationChange()
- runCurrent()
-
- assertThat(blueprint?.id).isEqualTo(SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID)
- }
- }
-
- @Test
- @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
- fun testAppliesWeatherClockBlueprint() {
- testScope.runTest {
- val blueprint by collectLastValue(underTest.blueprint)
- whenever(clockController.config)
- .thenReturn(
- ClockConfig(
- id = "DIGITAL_CLOCK_WEATHER",
- name = "clock",
- description = "clock",
- )
- )
- clockRepository.setCurrentClock(clockController)
- overrideResource(R.bool.config_use_split_notification_shade, false)
- configurationRepository.onConfigurationChange()
- runCurrent()
-
- assertThat(blueprint?.id).isEqualTo(WEATHER_CLOCK_BLUEPRINT_ID)
- }
- }
-
- @Test
@EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun testDoesNotApplySplitShadeBlueprint() {
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
index 3f05bfa..9ccf212 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
@@ -19,6 +19,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -227,4 +228,50 @@
{ it == KeyguardSurfaceBehindModel(alpha = 0f) },
)
}
+
+ @Test
+ fun notificationLaunchFromLockscreen_isAnimatingSurfaceTrue() =
+ testScope.runTest {
+ val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface)
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.FINISHED,
+ )
+ )
+ kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true)
+ runCurrent()
+ assertThat(isAnimatingSurface).isTrue()
+ }
+
+ @Test
+ fun notificationLaunchFromGone_isAnimatingSurfaceFalse() =
+ testScope.runTest {
+ val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface)
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ transitionState = TransitionState.FINISHED,
+ )
+ )
+ kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true)
+ runCurrent()
+ assertThat(isAnimatingSurface).isFalse()
+ }
}
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 2c0a51835..085b70e 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
@@ -27,6 +27,8 @@
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.dock.DockManager
+import com.android.systemui.dock.fakeDockManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeCommandQueue
@@ -108,6 +110,7 @@
private val powerInteractor = kosmos.powerInteractor
private val communalInteractor = kosmos.communalInteractor
+ private val dockManager = kosmos.fakeDockManager
@Before
fun setUp() {
@@ -1230,6 +1233,38 @@
}
@Test
+ fun occludedToGlanceableHubWhenDocked() =
+ testScope.runTest {
+ // GIVEN a device on lockscreen
+ keyguardRepository.setKeyguardShowing(true)
+ runCurrent()
+
+ // GIVEN a prior transition has run to OCCLUDED
+ runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.OCCLUDED)
+ keyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+
+ // GIVEN device is docked
+ dockManager.setIsDocked(true)
+ dockManager.setDockEvent(DockManager.STATE_DOCKED)
+
+ // WHEN occlusion ends
+ keyguardRepository.setKeyguardOccluded(false)
+ runCurrent()
+
+ // THEN a transition to GLANCEABLE_HUB should occur
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromOccludedTransitionInteractor::class.simpleName,
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.GLANCEABLE_HUB,
+ animatorAssertion = { it.isNotNull() },
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun occludedToAlternateBouncer() =
testScope.runTest {
// GIVEN a prior transition has run to OCCLUDED
@@ -1354,6 +1389,30 @@
}
@Test
+ fun dreamingToPrimaryBouncer() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to DREAMING
+ keyguardRepository.setDreaming(true)
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
+ runCurrent()
+
+ // WHEN the primary bouncer is set to show
+ bouncerRepository.setPrimaryShow(true)
+ runCurrent()
+
+ // THEN a transition to PRIMARY_BOUNCER should occur
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = "FromDreamingTransitionInteractor",
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ animatorAssertion = { it.isNotNull() },
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun dreamingToAod() =
testScope.runTest {
// GIVEN a prior transition has run to DREAMING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 6d605a5..b1a8dd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -281,6 +281,14 @@
// Oh no, we're still surfaceBehindAnimating=true, but no longer transitioning to GONE.
transitionRepository.sendTransitionStep(
TransitionStep(
+ transitionState = TransitionState.CANCELED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
transitionState = TransitionState.STARTED,
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.AOD,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt
index 66aa572..5e3a142 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt
@@ -113,7 +113,8 @@
id = "WEATHER_CLOCK",
name = "",
description = "",
- useAlternateSmartspaceAODTransition = true
+ useAlternateSmartspaceAODTransition = true,
+ useCustomClockScene = true
)
whenever(clock.config).thenReturn(clockConfig)
}
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 9c7f254..9aee5b6 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
@@ -27,6 +27,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.sections.AccessibilityActionsSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
@@ -58,6 +59,7 @@
class DefaultKeyguardBlueprintTest : SysuiTestCase() {
private lateinit var underTest: DefaultKeyguardBlueprint
private lateinit var rootView: KeyguardRootView
+ @Mock private lateinit var accessibilityActionsSection: AccessibilityActionsSection
@Mock private lateinit var defaultIndicationAreaSection: DefaultIndicationAreaSection
@Mock private lateinit var mDefaultDeviceEntrySection: DefaultDeviceEntrySection
@Mock private lateinit var defaultShortcutsSection: DefaultShortcutsSection
@@ -81,6 +83,7 @@
rootView = KeyguardRootView(context, null)
underTest =
DefaultKeyguardBlueprint(
+ accessibilityActionsSection,
defaultIndicationAreaSection,
mDefaultDeviceEntrySection,
defaultShortcutsSection,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index b5f668c..4f2b690 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -38,7 +38,6 @@
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
@@ -86,7 +85,6 @@
{ mock(DeviceEntryBackgroundViewModel::class.java) },
{ falsingManager },
{ mock(VibratorHelper::class.java) },
- mock(CoroutineDispatcher::class.java),
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
index d410dac..f1c93c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -32,7 +32,6 @@
import com.google.common.collect.Range
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
@@ -67,7 +66,7 @@
fun transitionToAlternateBouncer_scrimAlphaUpdate() =
testScope.runTest {
val scrimAlphas by collectValues(underTest.scrimAlpha)
- runCurrent()
+ assertThat(scrimAlphas.size).isEqualTo(1) // initial value is 0f
transitionRepository.sendTransitionSteps(
listOf(
@@ -79,7 +78,7 @@
testScope,
)
- assertThat(scrimAlphas.size).isEqualTo(4)
+ assertThat(scrimAlphas.size).isEqualTo(5)
scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
}
@@ -87,7 +86,7 @@
fun transitionFromAlternateBouncer_scrimAlphaUpdate() =
testScope.runTest {
val scrimAlphas by collectValues(underTest.scrimAlpha)
- runCurrent()
+ assertThat(scrimAlphas.size).isEqualTo(1) // initial value is 0f
transitionRepository.sendTransitionSteps(
listOf(
@@ -98,7 +97,7 @@
),
testScope,
)
- assertThat(scrimAlphas.size).isEqualTo(4)
+ assertThat(scrimAlphas.size).isEqualTo(5)
scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
index 143c4da..1396b20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
@@ -57,11 +57,18 @@
stepFromAlternateBouncer(0f, TransitionState.STARTED),
stepFromAlternateBouncer(.4f),
stepFromAlternateBouncer(.6f),
- stepFromAlternateBouncer(1f),
),
testScope,
)
assertThat(alternateBouncerWindowRequired).isTrue()
+
+ transitionRepository.sendTransitionSteps(
+ listOf(
+ stepFromAlternateBouncer(1.0f, TransitionState.FINISHED),
+ ),
+ testScope,
+ )
+ assertThat(alternateBouncerWindowRequired).isFalse()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
index 7b5dd1f..01754c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,191 +12,235 @@
* 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.viewmodel
-import android.provider.Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.filters.SmallTest
-import com.android.keyguard.ClockEventController
-import com.android.keyguard.KeyguardClockSwitch.LARGE
-import com.android.keyguard.KeyguardClockSwitch.SMALL
+import com.android.keyguard.KeyguardClockSwitch
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.KeyguardClockRepository
-import com.android.systemui.keyguard.data.repository.KeyguardClockRepositoryImpl
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.shared.ComposeLockscreen
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
+import com.android.systemui.keyguard.data.repository.keyguardClockRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.shared.model.SettingsClockSize
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.res.R
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.shared.model.ShadeMode
-import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
+import com.android.systemui.testKosmos
import com.android.systemui.util.Utils
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.mock
@SmallTest
@RunWith(JUnit4::class)
+@DisableSceneContainer
class KeyguardClockViewModelTest : SysuiTestCase() {
- private lateinit var scheduler: TestCoroutineScheduler
- private lateinit var dispatcher: CoroutineDispatcher
- private lateinit var scope: TestScope
+ private lateinit var kosmos: Kosmos
private lateinit var underTest: KeyguardClockViewModel
- private lateinit var keyguardInteractor: KeyguardInteractor
- private lateinit var keyguardRepository: KeyguardRepository
- private lateinit var keyguardClockInteractor: KeyguardClockInteractor
- private lateinit var keyguardClockRepository: KeyguardClockRepository
- private lateinit var fakeSettings: FakeSettings
- private val shadeMode = MutableStateFlow<ShadeMode>(ShadeMode.Single)
- @Mock private lateinit var clockRegistry: ClockRegistry
- @Mock private lateinit var clock: ClockController
- @Mock private lateinit var largeClock: ClockFaceController
- @Mock private lateinit var clockFaceConfig: ClockFaceConfig
- @Mock private lateinit var eventController: ClockEventController
- @Mock private lateinit var notifsKeyguardInteractor: NotificationsKeyguardInteractor
- @Mock private lateinit var areNotificationsFullyHidden: Flow<Boolean>
- @Mock private lateinit var shadeInteractor: ShadeInteractor
+ private lateinit var testScope: TestScope
+ private lateinit var clockController: ClockController
+ private lateinit var config: ClockFaceConfig
@Before
fun setup() {
- MockitoAnnotations.initMocks(this)
- KeyguardInteractorFactory.create().let {
- keyguardInteractor = it.keyguardInteractor
- keyguardRepository = it.repository
- }
- fakeSettings = FakeSettings()
- scheduler = TestCoroutineScheduler()
- dispatcher = StandardTestDispatcher(scheduler)
- scope = TestScope(dispatcher)
- setupMockClock()
- keyguardClockRepository =
- KeyguardClockRepositoryImpl(
- fakeSettings,
- clockRegistry,
- eventController,
- dispatcher,
- scope.backgroundScope
- )
- keyguardClockInteractor = KeyguardClockInteractor(keyguardClockRepository)
- whenever(notifsKeyguardInteractor.areNotificationsFullyHidden)
- .thenReturn(areNotificationsFullyHidden)
- whenever(shadeInteractor.shadeMode).thenReturn(shadeMode)
- underTest =
- KeyguardClockViewModel(
- keyguardInteractor,
- keyguardClockInteractor,
- scope.backgroundScope,
- notifsKeyguardInteractor,
- shadeInteractor,
- )
+ kosmos = testKosmos()
+ testScope = kosmos.testScope
+ underTest = kosmos.keyguardClockViewModel
+
+ clockController = mock(ClockController::class.java)
+ val largeClock = mock(ClockFaceController::class.java)
+ config = mock(ClockFaceConfig::class.java)
+
+ whenever(clockController.largeClock).thenReturn(largeClock)
+ whenever(largeClock.config).thenReturn(config)
}
@Test
- fun testClockSize_alwaysSmallClock() =
- scope.runTest {
- // When use double line clock is disabled,
- // should always return small
- fakeSettings.putInt(LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 0)
- keyguardClockRepository.setClockSize(LARGE)
- val value = collectLastValue(underTest.clockSize)
- assertThat(value()).isEqualTo(SMALL)
+ fun currentClockLayout_splitShadeOn_clockCentered_largeClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Split)
+ keyguardRepository.setClockShouldBeCentered(true)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.LARGE_CLOCK)
+ }
+
+ @Test
+ fun currentClockLayout_splitShadeOn_clockNotCentered_largeClock_splitShadeLargeClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Split)
+ keyguardRepository.setClockShouldBeCentered(false)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout)
+ .isEqualTo(KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK)
+ }
+
+ @Test
+ fun currentClockLayout_splitShadeOn_clockNotCentered_smallClock_splitShadeSmallClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Split)
+ keyguardRepository.setClockShouldBeCentered(false)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout)
+ .isEqualTo(KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_SMALL_CLOCK)
+ }
+
+ @Test
+ fun currentClockLayout_singleShade_smallClock_smallClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Single)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.SMALL_CLOCK)
+ }
+
+ @Test
+ fun currentClockLayout_singleShade_largeClock_largeClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Single)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.LARGE_CLOCK)
+ }
+
+ @Test
+ fun hasCustomPositionUpdatedAnimation_withConfigTrue_isTrue() =
+ testScope.runTest {
+ with(kosmos) {
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+ whenever(config.hasCustomPositionUpdatedAnimation).thenReturn(true)
+ fakeKeyguardClockRepository.setCurrentClock(clockController)
+ }
+
+ val hasCustomPositionUpdatedAnimation by
+ collectLastValue(underTest.hasCustomPositionUpdatedAnimation)
+ assertThat(hasCustomPositionUpdatedAnimation).isEqualTo(true)
+ }
+
+ @Test
+ fun hasCustomPositionUpdatedAnimation_withConfigFalse_isFalse() =
+ testScope.runTest {
+ with(kosmos) {
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+
+ whenever(config.hasCustomPositionUpdatedAnimation).thenReturn(false)
+ fakeKeyguardClockRepository.setCurrentClock(clockController)
+ }
+
+ val hasCustomPositionUpdatedAnimation by
+ collectLastValue(underTest.hasCustomPositionUpdatedAnimation)
+ assertThat(hasCustomPositionUpdatedAnimation).isEqualTo(false)
+ }
+
+ @Test
+ fun testClockSize_alwaysSmallClockSize() =
+ testScope.runTest {
+ kosmos.fakeKeyguardClockRepository.setSelectedClockSize(SettingsClockSize.SMALL)
+ kosmos.keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+
+ val value by collectLastValue(underTest.clockSize)
+ assertThat(value).isEqualTo(KeyguardClockSwitch.SMALL)
}
@Test
fun testClockSize_dynamicClockSize() =
- scope.runTest {
- fakeSettings.putInt(LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1)
- keyguardClockRepository.setClockSize(SMALL)
- var value = collectLastValue(underTest.clockSize)
- assertThat(value()).isEqualTo(SMALL)
+ testScope.runTest {
+ kosmos.keyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
+ kosmos.fakeKeyguardClockRepository.setSelectedClockSize(SettingsClockSize.DYNAMIC)
+ val value by collectLastValue(underTest.clockSize)
+ assertThat(value).isEqualTo(KeyguardClockSwitch.SMALL)
- keyguardClockRepository.setClockSize(LARGE)
- value = collectLastValue(underTest.clockSize)
- assertThat(value()).isEqualTo(LARGE)
+ kosmos.keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+ assertThat(value).isEqualTo(KeyguardClockSwitch.LARGE)
}
@Test
fun isLargeClockVisible_whenLargeClockSize_isTrue() =
- scope.runTest {
- fakeSettings.putInt(LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1)
- keyguardClockRepository.setClockSize(LARGE)
- var value = collectLastValue(underTest.isLargeClockVisible)
- assertThat(value()).isEqualTo(true)
+ testScope.runTest {
+ kosmos.keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+ val value by collectLastValue(underTest.isLargeClockVisible)
+ assertThat(value).isEqualTo(true)
}
@Test
fun isLargeClockVisible_whenSmallClockSize_isFalse() =
- scope.runTest {
- fakeSettings.putInt(LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1)
- keyguardClockRepository.setClockSize(SMALL)
- var value = collectLastValue(underTest.isLargeClockVisible)
- assertThat(value()).isEqualTo(false)
+ testScope.runTest {
+ kosmos.keyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
+ val value by collectLastValue(underTest.isLargeClockVisible)
+ assertThat(value).isEqualTo(false)
}
@Test
- fun testSmallClockTop_splitshade() =
- scope.runTest {
- shadeMode.value = ShadeMode.Split
- if (!ComposeLockscreen.isEnabled) {
- assertThat(underTest.getSmallClockTopMargin(context))
- .isEqualTo(
- context.resources.getDimensionPixelSize(
- R.dimen.keyguard_split_shade_top_margin
- )
- )
- } else {
- assertThat(underTest.getSmallClockTopMargin(context))
- .isEqualTo(
- context.resources.getDimensionPixelSize(
- R.dimen.keyguard_split_shade_top_margin
- ) - Utils.getStatusBarHeaderHeightKeyguard(context)
- )
- }
+ @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+ fun testSmallClockTop_splitShade_composeLockscreenOn() =
+ testScope.runTest {
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
+ assertThat(underTest.getSmallClockTopMargin(context))
+ .isEqualTo(
+ context.resources.getDimensionPixelSize(
+ R.dimen.keyguard_split_shade_top_margin
+ ) - Utils.getStatusBarHeaderHeightKeyguard(context)
+ )
}
@Test
- fun testSmallClockTop_nonSplitshade() =
- scope.runTest {
- if (!ComposeLockscreen.isEnabled) {
- assertThat(underTest.getSmallClockTopMargin(context))
- .isEqualTo(
- context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
- Utils.getStatusBarHeaderHeightKeyguard(context)
- )
- } else {
- assertThat(underTest.getSmallClockTopMargin(context))
- .isEqualTo(
- context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin)
- )
- }
+ @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+ fun testSmallClockTop_splitShade_composeLockscreenOff() =
+ testScope.runTest {
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
+ assertThat(underTest.getSmallClockTopMargin(context))
+ .isEqualTo(
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
+ )
}
- private fun setupMockClock() {
- whenever(clock.largeClock).thenReturn(largeClock)
- whenever(largeClock.config).thenReturn(clockFaceConfig)
- whenever(clockFaceConfig.hasCustomWeatherDataDisplay).thenReturn(false)
- whenever(clockRegistry.createCurrentClock()).thenReturn(clock)
- }
+ @Test
+ @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+ fun testSmallClockTop_nonSplitShade_composeLockscreenOn() =
+ testScope.runTest {
+ assertThat(underTest.getSmallClockTopMargin(context))
+ .isEqualTo(
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin)
+ )
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+ fun testSmallClockTop_nonSplitShade_composeLockscreenOff() =
+ testScope.runTest {
+ assertThat(underTest.getSmallClockTopMargin(context))
+ .isEqualTo(
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
+ Utils.getStatusBarHeaderHeightKeyguard(context)
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelWithKosmosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelWithKosmosTest.kt
deleted file mode 100644
index d12980a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelWithKosmosTest.kt
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.viewmodel
-
-import androidx.test.filters.SmallTest
-import com.android.keyguard.KeyguardClockSwitch
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
-import com.android.systemui.keyguard.data.repository.keyguardClockRepository
-import com.android.systemui.keyguard.data.repository.keyguardRepository
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.clocks.ClockController
-import com.android.systemui.plugins.clocks.ClockFaceConfig
-import com.android.systemui.plugins.clocks.ClockFaceController
-import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.shared.model.ShadeMode
-import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlin.test.Test
-import kotlinx.coroutines.test.runTest
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Mockito.mock
-
-@SmallTest
-@RunWith(JUnit4::class)
-class KeyguardClockViewModelWithKosmosTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val underTest = kosmos.keyguardClockViewModel
- private val testScope = kosmos.testScope
-
- @Test
- fun currentClockLayout_splitShadeOn_clockCentered_largeClock() =
- testScope.runTest {
- with(kosmos) {
- shadeRepository.setShadeMode(ShadeMode.Split)
- keyguardRepository.setClockShouldBeCentered(true)
- keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
- }
- val currentClockLayout by collectLastValue(underTest.currentClockLayout)
- assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.LARGE_CLOCK)
- }
-
- @Test
- fun currentClockLayout_splitShadeOn_clockNotCentered_largeClock_splitShadeLargeClock() =
- testScope.runTest {
- with(kosmos) {
- shadeRepository.setShadeMode(ShadeMode.Split)
- keyguardRepository.setClockShouldBeCentered(false)
- keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
- }
- val currentClockLayout by collectLastValue(underTest.currentClockLayout)
- assertThat(currentClockLayout)
- .isEqualTo(KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK)
- }
-
- @Test
- fun currentClockLayout_splitShadeOn_clockNotCentered_smallClock_splitShadeSmallClock() =
- testScope.runTest {
- with(kosmos) {
- shadeRepository.setShadeMode(ShadeMode.Split)
- keyguardRepository.setClockShouldBeCentered(false)
- keyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
- }
- val currentClockLayout by collectLastValue(underTest.currentClockLayout)
- assertThat(currentClockLayout)
- .isEqualTo(KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_SMALL_CLOCK)
- }
-
- @Test
- fun currentClockLayout_singleShade_smallClock_smallClock() =
- testScope.runTest {
- with(kosmos) {
- shadeRepository.setShadeMode(ShadeMode.Single)
- keyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
- }
- val currentClockLayout by collectLastValue(underTest.currentClockLayout)
- assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.SMALL_CLOCK)
- }
-
- @Test
- fun currentClockLayout_singleShade_largeClock_largeClock() =
- testScope.runTest {
- with(kosmos) {
- shadeRepository.setShadeMode(ShadeMode.Single)
- keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
- }
- val currentClockLayout by collectLastValue(underTest.currentClockLayout)
- assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.LARGE_CLOCK)
- }
-
- @Test
- fun hasCustomPositionUpdatedAnimation_withConfigTrue_isTrue() =
- testScope.runTest {
- with(kosmos) {
- keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
- fakeKeyguardClockRepository.setCurrentClock(
- buildClockController(hasCustomPositionUpdatedAnimation = true)
- )
- }
-
- val hasCustomPositionUpdatedAnimation by
- collectLastValue(underTest.hasCustomPositionUpdatedAnimation)
- assertThat(hasCustomPositionUpdatedAnimation).isEqualTo(true)
- }
-
- @Test
- fun hasCustomPositionUpdatedAnimation_withConfigFalse_isFalse() =
- testScope.runTest {
- with(kosmos) {
- keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
- fakeKeyguardClockRepository.setCurrentClock(
- buildClockController(hasCustomPositionUpdatedAnimation = false)
- )
- }
-
- val hasCustomPositionUpdatedAnimation by
- collectLastValue(underTest.hasCustomPositionUpdatedAnimation)
- assertThat(hasCustomPositionUpdatedAnimation).isEqualTo(false)
- }
-
- private fun buildClockController(
- hasCustomPositionUpdatedAnimation: Boolean = false
- ): ClockController {
- val clockController = mock(ClockController::class.java)
- val largeClock = mock(ClockFaceController::class.java)
- val config = mock(ClockFaceConfig::class.java)
-
- whenever(clockController.largeClock).thenReturn(largeClock)
- whenever(largeClock.config).thenReturn(config)
- whenever(config.hasCustomPositionUpdatedAnimation)
- .thenReturn(hasCustomPositionUpdatedAnimation)
-
- return clockController
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index b70cc30..8f73811 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -29,7 +29,9 @@
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_RESUME
import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
import com.android.systemui.media.controls.ui.controller.MediaPlayerData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
@@ -53,7 +55,6 @@
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -76,10 +77,10 @@
@TestableLooper.RunWithLooper
class MediaDataFilterImplTest : SysuiTestCase() {
- @Mock private lateinit var listener: MediaDataFilterImpl.Listener
+ @Mock private lateinit var listener: MediaDataProcessor.Listener
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var broadcastSender: BroadcastSender
- @Mock private lateinit var mediaDataManager: MediaDataManager
+ @Mock private lateinit var mediaDataProcessor: MediaDataProcessor
@Mock private lateinit var lockscreenUserManager: NotificationLockscreenUserManager
@Mock private lateinit var executor: Executor
@Mock private lateinit var smartspaceData: SmartspaceMediaData
@@ -89,7 +90,7 @@
@Mock private lateinit var cardAction: SmartspaceAction
private lateinit var mediaDataFilter: MediaDataFilterImpl
- private lateinit var mediaFilterRepository: MediaFilterRepository
+ private lateinit var repository: MediaFilterRepository
private lateinit var testScope: TestScope
private lateinit var dataMain: MediaData
private lateinit var dataGuest: MediaData
@@ -102,7 +103,7 @@
MediaPlayerData.clear()
whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
testScope = TestScope()
- mediaFilterRepository = MediaFilterRepository()
+ repository = MediaFilterRepository(FakeSystemClock())
mediaDataFilter =
MediaDataFilterImpl(
context,
@@ -113,9 +114,9 @@
clock,
logger,
mediaFlags,
- mediaFilterRepository,
+ repository,
)
- mediaDataFilter.mediaDataManager = mediaDataManager
+ mediaDataFilter.mediaDataProcessor = mediaDataProcessor
mediaDataFilter.addListener(listener)
// Start all tests as main user
@@ -162,91 +163,136 @@
}
@Test
- fun testOnDataLoadedForCurrentUser_callsListener() {
- // GIVEN a media for main user
- mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+ fun onDataLoadedForCurrentUser_updatesLoadedStates() =
+ testScope.runTest {
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
+ val mediaDataLoadingModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
- // THEN we should tell the listener
- verify(listener).onMediaDataLoaded(eq(dataMain.instanceId), eq(true), eq(0), eq(false))
- }
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+
+ verify(listener)
+ .onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true), eq(0), eq(false))
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaDataLoadingModel)
+ }
@Test
- fun testOnDataLoadedForGuest_doesNotCallListener() {
- // GIVEN a media for guest user
- mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
+ fun onDataLoadedForGuest_doesNotUpdateLoadedStates() =
+ testScope.runTest {
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
+ val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
- // THEN we should NOT tell the listener
- verify(listener, never()).onMediaDataLoaded(any(), anyBoolean(), anyInt(), anyBoolean())
- }
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
+
+ verify(listener, never())
+ .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+ assertThat(mediaDataLoadedStates).isNotEqualTo(mediaLoadedStatesModel)
+ }
@Test
- fun testOnRemovedForCurrent_callsListener() {
- // GIVEN a media was removed for main user
- mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
- mediaDataFilter.onMediaDataRemoved(KEY)
+ fun onRemovedForCurrent_updatesLoadedStates() =
+ testScope.runTest {
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
+ val mediaLoadedStatesModel =
+ mutableListOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
- // THEN we should tell the listener
- verify(listener).onMediaDataRemoved(eq(dataMain.instanceId))
- }
+ // GIVEN a media was removed for main user
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
+
+ mediaLoadedStatesModel.remove(MediaDataLoadingModel.Loaded(dataMain.instanceId))
+ mediaDataFilter.onMediaDataRemoved(KEY)
+
+ verify(listener).onMediaDataRemoved(eq(KEY))
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
+ }
@Test
- fun testOnRemovedForGuest_doesNotCallListener() {
- // GIVEN a media was removed for guest user
- mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
- mediaDataFilter.onMediaDataRemoved(KEY)
+ fun onRemovedForGuest_doesNotUpdateLoadedStates() =
+ testScope.runTest {
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
- // THEN we should NOT tell the listener
- verify(listener, never()).onMediaDataRemoved(eq(dataGuest.instanceId))
- }
+ // GIVEN a media was removed for guest user
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
+ mediaDataFilter.onMediaDataRemoved(KEY)
+
+ verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ assertThat(mediaDataLoadedStates).isEmpty()
+ }
@Test
- fun testOnUserSwitched_removesOldUserControls() {
- // GIVEN that we have a media loaded for main user
- mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+ fun onUserSwitched_removesOldUserControls() =
+ testScope.runTest {
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
+ val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
- // and we switch to guest user
- setUser(USER_GUEST)
+ // GIVEN that we have a media loaded for main user
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
- // THEN we should remove the main user's media
- verify(listener).onMediaDataRemoved(eq(dataMain.instanceId))
- }
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
+
+ // and we switch to guest user
+ setUser(USER_GUEST)
+
+ // THEN we should remove the main user's media
+ verify(listener).onMediaDataRemoved(eq(KEY))
+ assertThat(mediaDataLoadedStates).isEmpty()
+ }
@Test
- fun testOnUserSwitched_addsNewUserControls() {
- // GIVEN that we had some media for both users
- mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
- mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataGuest)
- reset(listener)
+ fun onUserSwitched_addsNewUserControls() =
+ testScope.runTest {
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
+ val guestLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataGuest.instanceId))
+ val mainLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
- // and we switch to guest user
- setUser(USER_GUEST)
+ // GIVEN that we had some media for both users
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+ mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataGuest)
- // THEN we should add back the guest user media
- verify(listener).onMediaDataLoaded(eq(dataGuest.instanceId), eq(true), eq(0), eq(false))
+ // and we switch to guest user
+ setUser(USER_GUEST)
- // but not the main user's
- verify(listener, never())
- .onMediaDataLoaded(eq(dataMain.instanceId), anyBoolean(), anyInt(), anyBoolean())
- }
+ // THEN we should add back the guest user media
+ verify(listener)
+ .onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true), eq(0), eq(false))
+
+ // but not the main user's
+ verify(listener, never())
+ .onMediaDataLoaded(
+ eq(KEY),
+ any(),
+ eq(dataMain),
+ anyBoolean(),
+ anyInt(),
+ anyBoolean()
+ )
+ assertThat(mediaDataLoadedStates).isEqualTo(guestLoadedStatesModel)
+ assertThat(mediaDataLoadedStates).isNotEqualTo(mainLoadedStatesModel)
+ }
@Test
- fun testOnProfileChanged_profileUnavailable_loadControls() {
- // GIVEN that we had some media for both profiles
- mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
- mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataPrivateProfile)
- reset(listener)
+ fun onProfileChanged_profileUnavailable_updateStates() =
+ testScope.runTest {
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
- // and we change profile status
- setPrivateProfileUnavailable()
+ // GIVEN that we had some media for both profiles
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+ mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataPrivateProfile)
- // THEN we should add the private profile media
- verify(listener).onMediaDataRemoved(eq(dataPrivateProfile.instanceId))
- }
+ // and we change profile status
+ setPrivateProfileUnavailable()
+
+ val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
+ // THEN we should remove the private profile media
+ verify(listener).onMediaDataRemoved(eq(KEY_ALT))
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
+ }
@Test
fun hasAnyMedia_mediaSet_returnsTrue() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
assertThat(hasAnyMedia(selectedUserEntries)).isTrue()
@@ -255,7 +301,7 @@
@Test
fun hasAnyMedia_recommendationSet_returnsFalse() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
assertThat(hasAnyMedia(selectedUserEntries)).isFalse()
@@ -264,8 +310,8 @@
@Test
fun hasAnyMediaOrRecommendation_mediaSet_returnsTrue() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData))
@@ -275,8 +321,8 @@
@Test
fun hasAnyMediaOrRecommendation_recommendationSet_returnsTrue() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData))
@@ -286,7 +332,7 @@
@Test
fun hasActiveMedia_inactiveMediaSet_returnsFalse() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
val data = dataMain.copy(active = false)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
@@ -297,7 +343,7 @@
@Test
fun hasActiveMedia_activeMediaSet_returnsTrue() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
val data = dataMain.copy(active = true)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
@@ -307,9 +353,9 @@
@Test
fun hasActiveMediaOrRecommendation_inactiveMediaSet_returnsFalse() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
val data = dataMain.copy(active = false)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
@@ -326,9 +372,9 @@
@Test
fun hasActiveMediaOrRecommendation_activeMediaSet_returnsTrue() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
val data = dataMain.copy(active = true)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
@@ -345,9 +391,9 @@
@Test
fun hasActiveMediaOrRecommendation_inactiveRecommendationSet_returnsFalse() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
whenever(smartspaceData.isActive).thenReturn(false)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -364,9 +410,9 @@
@Test
fun hasActiveMediaOrRecommendation_invalidRecommendationSet_returnsFalse() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
whenever(smartspaceData.isValid()).thenReturn(false)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -383,9 +429,9 @@
@Test
fun hasActiveMediaOrRecommendation_activeAndValidRecommendationSet_returnsTrue() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
whenever(smartspaceData.isActive).thenReturn(true)
whenever(smartspaceData.isValid()).thenReturn(true)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -401,10 +447,10 @@
}
@Test
- fun testHasAnyMediaOrRecommendation_onlyCurrentUser() =
+ fun hasAnyMediaOrRecommendation_onlyCurrentUser() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData))
.isFalse()
@@ -415,11 +461,11 @@
}
@Test
- fun testHasActiveMediaOrRecommendation_onlyCurrentUser() =
+ fun hasActiveMediaOrRecommendation_onlyCurrentUser() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -443,10 +489,10 @@
}
@Test
- fun testOnNotificationRemoved_doesNotHaveMedia() =
+ fun onNotificationRemoved_doesNotHaveMedia() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
mediaDataFilter.onMediaDataRemoved(KEY)
@@ -456,23 +502,27 @@
}
@Test
- fun testOnSwipeToDismiss_setsTimedOut() {
+ fun onSwipeToDismiss_setsTimedOut() {
mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
mediaDataFilter.onSwipeToDismiss()
- verify(mediaDataManager).setInactive(eq(KEY), eq(true), eq(true))
+ verify(mediaDataProcessor).setInactive(eq(KEY), eq(true), eq(true))
}
@Test
- fun testOnSmartspaceMediaDataLoaded_noMedia_activeValidRec_prioritizesSmartspace() =
+ fun onSmartspaceMediaDataLoaded_noMedia_activeValidRec_prioritizesSmartspace() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
+ val recommendationsLoadingModel =
+ SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY, isPrioritized = true)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(true))
+ assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -482,23 +532,26 @@
)
.isTrue()
assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
+ verify(listener)
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
verify(logger, never()).logRecommendationActivated(any(), any(), any())
}
@Test
- fun testOnSmartspaceMediaDataLoaded_noMedia_inactiveRec_showsNothing() =
+ fun onSmartspaceMediaDataLoaded_noMedia_inactiveRec_showsNothing() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
whenever(smartspaceData.isActive).thenReturn(false)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- verify(listener, never()).onMediaDataLoaded(any(), anyBoolean(), anyInt(), anyBoolean())
- verify(listener, never()).onSmartspaceMediaDataLoaded(any(), anyBoolean())
+ assertThat(recommendationsLoadingState).isEqualTo(SmartspaceMediaLoadingModel.Unknown)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -508,22 +561,29 @@
)
.isFalse()
assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
+ verify(listener, never())
+ .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+ verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
verify(logger, never()).logRecommendationAdded(any(), any())
verify(logger, never()).logRecommendationActivated(any(), any(), any())
}
@Test
- fun testOnSmartspaceMediaDataLoaded_noRecentMedia_activeValidRec_prioritizesSmartspace() =
+ fun onSmartspaceMediaDataLoaded_noRecentMedia_activeValidRec_prioritizesSmartspace() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
+ val recommendationsLoadingModel =
+ SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY, isPrioritized = true)
val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
clock.advanceTime(MediaDataFilterImpl.SMARTSPACE_MAX_AGE + 100)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(true))
+ assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -533,16 +593,20 @@
)
.isTrue()
assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
+ verify(listener)
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
verify(logger, never()).logRecommendationActivated(any(), any(), any())
}
@Test
- fun testOnSmartspaceMediaDataLoaded_noRecentMedia_inactiveRec_showsNothing() =
+ fun onSmartspaceMediaDataLoaded_noRecentMedia_inactiveRec_showsNothing() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
whenever(smartspaceData.isActive).thenReturn(false)
val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
@@ -550,7 +614,7 @@
clock.advanceTime(MediaDataFilterImpl.SMARTSPACE_MAX_AGE + 100)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- verify(listener, never()).onSmartspaceMediaDataLoaded(any(), anyBoolean())
+ assertThat(recommendationsLoadingState).isEqualTo(SmartspaceMediaLoadingModel.Unknown)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -560,32 +624,37 @@
)
.isFalse()
assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
+ verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
verify(logger, never()).logRecommendationAdded(any(), any())
verify(logger, never()).logRecommendationActivated(any(), any(), any())
}
@Test
- fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_inactiveRec_showsNothing() =
+ fun onSmartspaceMediaDataLoaded_hasRecentMedia_inactiveRec_showsNothing() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
whenever(smartspaceData.isActive).thenReturn(false)
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
+ val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
- verify(listener)
- .onMediaDataLoaded(eq(dataCurrent.instanceId), eq(true), eq(0), eq(false))
- reset(listener)
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
+ verify(listener)
+ .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
+
// AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- // THEN we should tell listeners to treat the media as not active instead
- verify(listener, never()).onMediaDataLoaded(any(), anyBoolean(), anyInt(), anyBoolean())
- verify(listener, never()).onSmartspaceMediaDataLoaded(any(), anyBoolean())
+ // THEN we should treat the media as not active instead
+ assertThat(recommendationsLoadingState).isEqualTo(SmartspaceMediaLoadingModel.Unknown)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -595,32 +664,39 @@
)
.isFalse()
assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
+ verify(listener, never())
+ .onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(), anyInt(), anyBoolean())
+ verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
verify(logger, never()).logRecommendationAdded(any(), any())
verify(logger, never()).logRecommendationActivated(any(), any(), any())
}
@Test
- fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedia() =
+ fun onSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedia() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
whenever(smartspaceData.isValid()).thenReturn(false)
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
+ val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
verify(listener)
- .onMediaDataLoaded(eq(dataCurrent.instanceId), eq(true), eq(0), eq(false))
+ .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
// AND we get a smartspace signal
runCurrent()
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- // THEN we should tell listeners to treat the media as active instead
+ // THEN we should treat the media as active instead
val dataCurrentAndActive = dataCurrent.copy(active = true)
- verify(listener)
- .onMediaDataLoaded(eq(dataCurrentAndActive.instanceId), eq(true), eq(100), eq(true))
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -629,32 +705,58 @@
)
)
.isTrue()
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(KEY),
+ eq(dataCurrentAndActive),
+ eq(true),
+ eq(100),
+ eq(true)
+ )
// Smartspace update shouldn't be propagated for the empty rec list.
- verify(listener, never()).onSmartspaceMediaDataLoaded(any(), anyBoolean())
+ assertThat(recommendationsLoadingState).isEqualTo(SmartspaceMediaLoadingModel.Unknown)
+ verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
verify(logger, never()).logRecommendationAdded(any(), any())
verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID))
}
@Test
- fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeValidRec_usesBoth() =
+ fun onSmartspaceMediaDataLoaded_hasRecentMedia_activeValidRec_usesBoth() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
+ val mediaDataLoadingModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
+ val recommendationsLoadingModel = SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
+
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaDataLoadingModel)
verify(listener)
- .onMediaDataLoaded(eq(dataCurrent.instanceId), eq(true), eq(0), eq(false))
+ .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
// AND we get a smartspace signal
runCurrent()
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- // THEN we should tell listeners to treat the media as active instead
+ // THEN we should treat the media as active instead
val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener)
- .onMediaDataLoaded(eq(dataCurrentAndActive.instanceId), eq(true), eq(100), eq(true))
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(KEY),
+ eq(dataCurrentAndActive),
+ eq(true),
+ eq(100),
+ eq(true)
+ )
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaDataLoadingModel)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -664,22 +766,28 @@
)
.isTrue()
// Smartspace update should also be propagated but not prioritized.
- verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(false))
+ assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
+ verify(listener)
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID))
}
@Test
- fun testOnSmartspaceMediaDataRemoved_usedSmartspace_clearsSmartspace() =
+ fun onSmartspaceMediaDataRemoved_usedSmartspace_clearsSmartspace() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
+ val recommendationsLoadingModel = SmartspaceMediaLoadingModel.Removed(SMARTSPACE_KEY)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
+ assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -692,26 +800,42 @@
}
@Test
- fun testOnSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBoth() =
+ fun onSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBoth() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
+ val recommendationsLoadingModel = SmartspaceMediaLoadingModel.Removed(SMARTSPACE_KEY)
+ val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
verify(listener)
- .onMediaDataLoaded(eq(dataCurrent.instanceId), eq(true), eq(0), eq(false))
+ .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
runCurrent()
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener)
- .onMediaDataLoaded(eq(dataCurrentAndActive.instanceId), eq(true), eq(100), eq(true))
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(KEY),
+ eq(dataCurrentAndActive),
+ eq(true),
+ eq(100),
+ eq(true)
+ )
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
+ assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -724,17 +848,22 @@
}
@Test
- fun testOnSmartspaceLoaded_persistentEnabled_isInactive_notifiesListeners() =
+ fun onSmartspaceLoaded_persistentEnabled_isInactive() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
+ val recommendationsLoadingModel = SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
whenever(smartspaceData.isActive).thenReturn(false)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(false))
+ verify(listener)
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+ assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -748,11 +877,16 @@
}
@Test
- fun testOnSmartspaceLoaded_persistentEnabled_inactive_hasRecentMedia_staysInactive() =
+ fun onSmartspaceLoaded_persistentEnabled_inactive_hasRecentMedia_staysInactive() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
+ val recommendationsLoadingModel = SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
+ val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
whenever(smartspaceData.isActive).thenReturn(false)
@@ -760,16 +894,20 @@
// If there is media that was recently played but inactive
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
- verify(listener)
- .onMediaDataLoaded(eq(dataCurrent.instanceId), eq(true), eq(0), eq(false))
- reset(listener)
+ verify(listener)
+ .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
+
// And an inactive recommendation is loaded
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
// Smartspace is loaded but the media stays inactive
- verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(false))
- verify(listener, never()).onMediaDataLoaded(any(), anyBoolean(), anyInt(), anyBoolean())
+ verify(listener)
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+ verify(listener, never())
+ .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+ assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -783,7 +921,7 @@
}
@Test
- fun testOnSwipeToDismiss_persistentEnabled_recommendationSetInactive() {
+ fun onSwipeToDismiss_persistentEnabled_recommendationSetInactive() {
whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
val data =
@@ -796,22 +934,29 @@
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, data)
mediaDataFilter.onSwipeToDismiss()
- verify(mediaDataManager).setRecommendationInactive(eq(SMARTSPACE_KEY))
- verify(mediaDataManager, never())
+ verify(mediaDataProcessor).setRecommendationInactive(eq(SMARTSPACE_KEY))
+ verify(mediaDataProcessor, never())
.dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong())
}
@Test
- fun testSmartspaceLoaded_shouldTriggerResume_doesTrigger() =
+ fun smartspaceLoaded_shouldTriggerResume_doesTrigger() =
testScope.runTest {
- val selectedUserEntries by collectLastValue(mediaFilterRepository.selectedUserEntries)
- val smartspaceMediaData by collectLastValue(mediaFilterRepository.smartspaceMediaData)
- val reactivatedKey by collectLastValue(mediaFilterRepository.reactivatedId)
+ val selectedUserEntries by collectLastValue(repository.selectedUserEntries)
+ val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
+ val reactivatedKey by collectLastValue(repository.reactivatedId)
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
+ val recommendationsLoadingModel = SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
+ val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+
verify(listener)
- .onMediaDataLoaded(eq(dataCurrent.instanceId), eq(true), eq(0), eq(false))
+ .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
// AND we get a smartspace signal with extra to trigger resume
runCurrent()
@@ -819,10 +964,18 @@
whenever(cardAction.extras).thenReturn(extras)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- // THEN we should tell listeners to treat the media as active instead
+ // THEN we should treat the media as active instead
val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener)
- .onMediaDataLoaded(eq(dataCurrentAndActive.instanceId), eq(true), eq(100), eq(true))
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(KEY),
+ eq(dataCurrentAndActive),
+ eq(true),
+ eq(100),
+ eq(true)
+ )
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
assertThat(
hasActiveMediaOrRecommendation(
selectedUserEntries,
@@ -831,27 +984,42 @@
)
)
.isTrue()
- // And send the smartspace data, but not prioritized
- verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(false))
+ // And update the smartspace data state, but not prioritized
+ assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
+ verify(listener)
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
}
@Test
- fun testSmartspaceLoaded_notShouldTriggerResume_doesNotTrigger() {
- // WHEN we have media that was recently played, but not currently active
- val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
- mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
- verify(listener).onMediaDataLoaded(eq(dataCurrent.instanceId), eq(true), eq(0), eq(false))
+ fun smartspaceLoaded_notShouldTriggerResume_doesNotTrigger() =
+ testScope.runTest {
+ val mediaDataLoadedStates by collectLastValue(repository.mediaDataLoadedStates)
+ val recommendationsLoadingState by
+ collectLastValue(repository.recommendationsLoadingState)
+ val recommendationsLoadingModel = SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
+ val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
- // AND we get a smartspace signal with extra to not trigger resume
- val extras = Bundle().apply { putBoolean(EXTRA_KEY_TRIGGER_RESUME, false) }
- whenever(cardAction.extras).thenReturn(extras)
- mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+ // WHEN we have media that was recently played, but not currently active
+ val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
- // THEN listeners are not updated to show media
- verify(listener, never()).onMediaDataLoaded(any(), eq(true), eq(100), eq(true))
- // But the smartspace update is still propagated
- verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(false))
- }
+ verify(listener)
+ .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
+ assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
+
+ // AND we get a smartspace signal with extra to not trigger resume
+ val extras = Bundle().apply { putBoolean(EXTRA_KEY_TRIGGER_RESUME, false) }
+ whenever(cardAction.extras).thenReturn(extras)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+ // THEN listeners are not updated to show media
+ verify(listener, never())
+ .onMediaDataLoaded(eq(KEY), eq(KEY), any(), eq(true), eq(100), eq(true))
+ // But the smartspace update is still propagated
+ verify(listener)
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+ assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
+ }
private fun hasActiveMediaOrRecommendation(
entries: Map<InstanceId, MediaData>?,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 5c275b4..ffb50c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -210,7 +210,7 @@
)
testDispatcher = UnconfinedTestDispatcher()
testScope = TestScope(testDispatcher)
- mediaFilterRepository = MediaFilterRepository()
+ mediaFilterRepository = MediaFilterRepository(clock)
mediaDataRepository = MediaDataRepository(mediaFlags, dumpManager)
mediaDataProcessor =
MediaDataProcessor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
index a73bb2c..e5d3082 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
@@ -16,29 +16,54 @@
package com.android.systemui.media.controls.ui.controller
+import android.animation.AnimatorSet
+import android.content.Context
import android.content.res.Configuration
import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.RippleDrawable
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
+import android.view.ViewGroup
+import android.view.animation.Interpolator
+import android.widget.FrameLayout
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.SeekBar
+import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.lifecycle.LiveData
import androidx.test.filters.SmallTest
+import com.android.internal.widget.CachingIconView
import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.controls.ui.view.GutsViewHolder
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.res.R
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
+import com.android.systemui.surfaceeffects.ripple.MultiRippleView
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
import com.android.systemui.util.animation.MeasurementInput
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.animation.TransitionViewState
import com.android.systemui.util.animation.WidgetState
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.floatThat
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -55,6 +80,31 @@
com.android.systemui.statusbar.phone.ConfigurationControllerImpl(context)
private var player = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0)
private var recommendation = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0)
+ private val clock = FakeSystemClock()
+ private lateinit var mainExecutor: FakeExecutor
+ private lateinit var seekBar: SeekBar
+ private lateinit var multiRippleView: MultiRippleView
+ private lateinit var turbulenceNoiseView: TurbulenceNoiseView
+ private lateinit var loadingEffectView: LoadingEffectView
+ private lateinit var settings: ImageButton
+ private lateinit var cancel: View
+ private lateinit var cancelText: TextView
+ private lateinit var dismiss: FrameLayout
+ private lateinit var dismissText: TextView
+ private lateinit var titleText: TextView
+ private lateinit var artistText: TextView
+ private lateinit var explicitIndicator: CachingIconView
+ private lateinit var seamless: ViewGroup
+ private lateinit var seamlessButton: View
+ private lateinit var seamlessIcon: ImageView
+ private lateinit var seamlessText: TextView
+ private lateinit var scrubbingElapsedTimeView: TextView
+ private lateinit var scrubbingTotalTimeView: TextView
+ private lateinit var actionPlayPause: ImageButton
+ private lateinit var actionNext: ImageButton
+ private lateinit var actionPrev: ImageButton
+ @Mock private lateinit var seamlessBackground: RippleDrawable
+ @Mock private lateinit var albumView: ImageView
@Mock lateinit var logger: MediaViewLogger
@Mock private lateinit var mockViewState: TransitionViewState
@Mock private lateinit var mockCopiedState: TransitionViewState
@@ -64,6 +114,14 @@
@Mock private lateinit var mediaSubTitleWidgetState: WidgetState
@Mock private lateinit var mediaContainerWidgetState: WidgetState
@Mock private lateinit var mediaFlags: MediaFlags
+ @Mock private lateinit var seekBarViewModel: SeekBarViewModel
+ @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress>
+ @Mock private lateinit var globalSettings: GlobalSettings
+ @Mock private lateinit var viewHolder: MediaViewHolder
+ @Mock private lateinit var view: TransitionLayout
+ @Mock private lateinit var mockAnimator: AnimatorSet
+ @Mock private lateinit var gutsViewHolder: GutsViewHolder
+ @Mock private lateinit var gutsText: TextView
private val delta = 0.1F
@@ -72,14 +130,30 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ mainExecutor = FakeExecutor(clock)
mediaViewController =
- MediaViewController(
- context,
- configurationController,
- mediaHostStatesManager,
- logger,
- mediaFlags,
- )
+ object :
+ MediaViewController(
+ context,
+ configurationController,
+ mediaHostStatesManager,
+ logger,
+ seekBarViewModel,
+ mainExecutor,
+ mediaFlags,
+ globalSettings,
+ ) {
+ override fun loadAnimator(
+ context: Context,
+ animId: Int,
+ motionInterpolator: Interpolator?,
+ vararg targets: View?
+ ): AnimatorSet {
+ return mockAnimator
+ }
+ }
+ initGutsViewHolderMocks()
+ initMediaViewHolderMocks()
}
@Test
@@ -299,4 +373,270 @@
verify(mediaTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
}
+
+ @Test
+ fun attachPlayer_seekBarDisabled_seekBarVisibilityIsSetToInvisible() {
+ whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+ mediaViewController.attachPlayer(viewHolder)
+ getEnabledChangeListener().onEnabledChanged(enabled = true)
+ getEnabledChangeListener().onEnabledChanged(enabled = false)
+
+ assertThat(mediaViewController.expandedLayout.getVisibility(R.id.media_progress_bar))
+ .isEqualTo(ConstraintSet.INVISIBLE)
+ }
+
+ @Test
+ fun attachPlayer_seekBarEnabled_seekBarVisible() {
+ whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+ mediaViewController.attachPlayer(viewHolder)
+ getEnabledChangeListener().onEnabledChanged(enabled = true)
+
+ assertThat(mediaViewController.expandedLayout.getVisibility(R.id.media_progress_bar))
+ .isEqualTo(ConstraintSet.VISIBLE)
+ }
+
+ @Test
+ fun attachPlayer_seekBarStatusUpdate_seekBarVisibilityChanges() {
+ whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+ mediaViewController.attachPlayer(viewHolder)
+ getEnabledChangeListener().onEnabledChanged(enabled = true)
+
+ assertThat(mediaViewController.expandedLayout.getVisibility(R.id.media_progress_bar))
+ .isEqualTo(ConstraintSet.VISIBLE)
+
+ getEnabledChangeListener().onEnabledChanged(enabled = false)
+
+ assertThat(mediaViewController.expandedLayout.getVisibility(R.id.media_progress_bar))
+ .isEqualTo(ConstraintSet.INVISIBLE)
+ }
+
+ @Test
+ fun attachPlayer_notScrubbing_scrubbingViewsGone() {
+ whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+ mediaViewController.attachPlayer(viewHolder)
+ mediaViewController.canShowScrubbingTime = true
+ getScrubbingChangeListener().onScrubbingChanged(true)
+ getScrubbingChangeListener().onScrubbingChanged(false)
+ mainExecutor.runAllReady()
+
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+ )
+ .isEqualTo(ConstraintSet.GONE)
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+ )
+ .isEqualTo(ConstraintSet.GONE)
+ }
+
+ @Test
+ fun setIsScrubbing_noSemanticActions_scrubbingViewsGone() {
+ whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+ mediaViewController.attachPlayer(viewHolder)
+ mediaViewController.canShowScrubbingTime = false
+ getScrubbingChangeListener().onScrubbingChanged(true)
+ mainExecutor.runAllReady()
+
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+ )
+ .isEqualTo(ConstraintSet.GONE)
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+ )
+ .isEqualTo(ConstraintSet.GONE)
+ }
+
+ @Test
+ fun setIsScrubbing_noPrevButton_scrubbingTimesNotShown() {
+ whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+ mediaViewController.attachPlayer(viewHolder)
+ mediaViewController.setUpNextButtonInfo(true)
+ mediaViewController.setUpPrevButtonInfo(false)
+ getScrubbingChangeListener().onScrubbingChanged(true)
+ mainExecutor.runAllReady()
+
+ assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionNext))
+ .isEqualTo(ConstraintSet.VISIBLE)
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+ )
+ .isEqualTo(ConstraintSet.GONE)
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+ )
+ .isEqualTo(ConstraintSet.GONE)
+ }
+
+ @Test
+ fun setIsScrubbing_noNextButton_scrubbingTimesNotShown() {
+ whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+ mediaViewController.attachPlayer(viewHolder)
+ mediaViewController.setUpNextButtonInfo(false)
+ mediaViewController.setUpPrevButtonInfo(true)
+ getScrubbingChangeListener().onScrubbingChanged(true)
+ mainExecutor.runAllReady()
+
+ assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionPrev))
+ .isEqualTo(ConstraintSet.VISIBLE)
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+ )
+ .isEqualTo(ConstraintSet.GONE)
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+ )
+ .isEqualTo(ConstraintSet.GONE)
+ }
+
+ @Test
+ fun setIsScrubbing_scrubbingViewsShownAndPrevNextHiddenOnlyInExpanded() {
+ whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+ mediaViewController.attachPlayer(viewHolder)
+ mediaViewController.setUpNextButtonInfo(true)
+ mediaViewController.setUpPrevButtonInfo(true)
+ mediaViewController.canShowScrubbingTime = true
+ getScrubbingChangeListener().onScrubbingChanged(true)
+ mainExecutor.runAllReady()
+
+ // Only in expanded, we should show the scrubbing times and hide prev+next
+ assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionPrev))
+ .isEqualTo(ConstraintSet.GONE)
+ assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionNext))
+ .isEqualTo(ConstraintSet.GONE)
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+ )
+ .isEqualTo(ConstraintSet.VISIBLE)
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+ )
+ .isEqualTo(ConstraintSet.VISIBLE)
+ }
+
+ @Test
+ fun setIsScrubbing_trueThenFalse_reservePrevAndNextButtons() {
+ whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+ mediaViewController.attachPlayer(viewHolder)
+ mediaViewController.setUpNextButtonInfo(true, ConstraintSet.INVISIBLE)
+ mediaViewController.setUpPrevButtonInfo(true, ConstraintSet.INVISIBLE)
+ mediaViewController.canShowScrubbingTime = true
+
+ getScrubbingChangeListener().onScrubbingChanged(true)
+ mainExecutor.runAllReady()
+
+ assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionPrev))
+ .isEqualTo(ConstraintSet.INVISIBLE)
+ assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionNext))
+ .isEqualTo(ConstraintSet.INVISIBLE)
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+ )
+ .isEqualTo(ConstraintSet.VISIBLE)
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+ )
+ .isEqualTo(ConstraintSet.VISIBLE)
+
+ getScrubbingChangeListener().onScrubbingChanged(false)
+ mainExecutor.runAllReady()
+
+ // Only in expanded, we should hide the scrubbing times and show prev+next
+ assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionPrev))
+ .isEqualTo(ConstraintSet.VISIBLE)
+ assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionNext))
+ .isEqualTo(ConstraintSet.VISIBLE)
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+ )
+ .isEqualTo(ConstraintSet.GONE)
+ assertThat(
+ mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+ )
+ .isEqualTo(ConstraintSet.GONE)
+ }
+
+ private fun initGutsViewHolderMocks() {
+ settings = ImageButton(context)
+ cancel = View(context)
+ cancelText = TextView(context)
+ dismiss = FrameLayout(context)
+ dismissText = TextView(context)
+ whenever(gutsViewHolder.gutsText).thenReturn(gutsText)
+ whenever(gutsViewHolder.settings).thenReturn(settings)
+ whenever(gutsViewHolder.cancel).thenReturn(cancel)
+ whenever(gutsViewHolder.cancelText).thenReturn(cancelText)
+ whenever(gutsViewHolder.dismiss).thenReturn(dismiss)
+ whenever(gutsViewHolder.dismissText).thenReturn(dismissText)
+ }
+
+ private fun initMediaViewHolderMocks() {
+ titleText = TextView(context)
+ artistText = TextView(context)
+ explicitIndicator = CachingIconView(context).also { it.id = R.id.media_explicit_indicator }
+ seamless = FrameLayout(context)
+ seamless.foreground = seamlessBackground
+ seamlessButton = View(context)
+ seamlessIcon = ImageView(context)
+ seamlessText = TextView(context)
+ seekBar = SeekBar(context).also { it.id = R.id.media_progress_bar }
+
+ actionPlayPause = ImageButton(context).also { it.id = R.id.actionPlayPause }
+ actionPrev = ImageButton(context).also { it.id = R.id.actionPrev }
+ actionNext = ImageButton(context).also { it.id = R.id.actionNext }
+ scrubbingElapsedTimeView =
+ TextView(context).also { it.id = R.id.media_scrubbing_elapsed_time }
+ scrubbingTotalTimeView = TextView(context).also { it.id = R.id.media_scrubbing_total_time }
+
+ multiRippleView = MultiRippleView(context, null)
+ turbulenceNoiseView = TurbulenceNoiseView(context, null)
+ loadingEffectView = LoadingEffectView(context, null)
+
+ whenever(viewHolder.player).thenReturn(view)
+ whenever(view.context).thenReturn(context)
+ whenever(viewHolder.albumView).thenReturn(albumView)
+ whenever(albumView.foreground).thenReturn(Mockito.mock(Drawable::class.java))
+ whenever(viewHolder.titleText).thenReturn(titleText)
+ whenever(viewHolder.artistText).thenReturn(artistText)
+ whenever(viewHolder.explicitIndicator).thenReturn(explicitIndicator)
+ whenever(seamlessBackground.getDrawable(0))
+ .thenReturn(Mockito.mock(GradientDrawable::class.java))
+ whenever(viewHolder.seamless).thenReturn(seamless)
+ whenever(viewHolder.seamlessButton).thenReturn(seamlessButton)
+ whenever(viewHolder.seamlessIcon).thenReturn(seamlessIcon)
+ whenever(viewHolder.seamlessText).thenReturn(seamlessText)
+ whenever(viewHolder.seekBar).thenReturn(seekBar)
+ whenever(viewHolder.scrubbingElapsedTimeView).thenReturn(scrubbingElapsedTimeView)
+ whenever(viewHolder.scrubbingTotalTimeView).thenReturn(scrubbingTotalTimeView)
+ whenever(viewHolder.gutsViewHolder).thenReturn(gutsViewHolder)
+ whenever(seekBarViewModel.progress).thenReturn(seekBarData)
+
+ // Action buttons
+ whenever(viewHolder.actionPlayPause).thenReturn(actionPlayPause)
+ whenever(viewHolder.getAction(R.id.actionNext)).thenReturn(actionNext)
+ whenever(viewHolder.getAction(R.id.actionPrev)).thenReturn(actionPrev)
+ whenever(viewHolder.getAction(R.id.actionPlayPause)).thenReturn(actionPlayPause)
+
+ whenever(viewHolder.multiRippleView).thenReturn(multiRippleView)
+ whenever(viewHolder.turbulenceNoiseView).thenReturn(turbulenceNoiseView)
+ whenever(viewHolder.loadingEffectView).thenReturn(loadingEffectView)
+ }
+
+ private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener =
+ withArgCaptor {
+ verify(seekBarViewModel).setScrubbingChangeListener(capture())
+ }
+
+ private fun getEnabledChangeListener(): SeekBarViewModel.EnabledChangeListener = withArgCaptor {
+ verify(seekBarViewModel).setEnabledChangeListener(capture())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index ca403e0..9bb21f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -123,11 +123,22 @@
mMediaControllers.add(mMediaController);
when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
- mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotifCollection, mDialogTransitionAnimator,
- mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
- mKeyguardManager, mFlags, mUserTracker);
+ mMediaOutputController =
+ new MediaOutputController(
+ mContext,
+ TEST_PACKAGE,
+ mContext.getUser(),
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyguardManager,
+ mFlags,
+ mUserTracker);
// Using a fake package will cause routing operations to fail, so we intercept
// scanning-related operations.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
index c9eb67e..2e6388a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -124,11 +124,22 @@
when(mLocalBluetoothLeBroadcast.getBroadcastCode()).thenReturn(
BROADCAST_CODE_TEST.getBytes(StandardCharsets.UTF_8));
- mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotifCollection, mDialogTransitionAnimator,
- mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
- mKeyguardManager, mFlags, mUserTracker);
+ mMediaOutputController =
+ new MediaOutputController(
+ mContext,
+ TEST_PACKAGE,
+ mContext.getUser(),
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyguardManager,
+ mFlags,
+ mUserTracker);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputBroadcastDialog = new MediaOutputBroadcastDialog(mContext, false,
mBroadcastSender, mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 980eb59..4eb0038 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -194,11 +194,22 @@
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
mCachedBluetoothDeviceManager);
- mMediaOutputController = new MediaOutputController(mSpyContext, mPackageName,
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotifCollection, mDialogTransitionAnimator,
- mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
- mKeyguardManager, mFlags, mUserTracker);
+ mMediaOutputController =
+ new MediaOutputController(
+ mSpyContext,
+ mPackageName,
+ mContext.getUser(),
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyguardManager,
+ mFlags,
+ mUserTracker);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(false);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
@@ -276,11 +287,22 @@
@Test
public void start_withoutPackageName_verifyMediaControllerInit() {
- mMediaOutputController = new MediaOutputController(mSpyContext, null,
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotifCollection, mDialogTransitionAnimator,
- mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
- mKeyguardManager, mFlags, mUserTracker);
+ mMediaOutputController =
+ new MediaOutputController(
+ mSpyContext,
+ null,
+ mContext.getUser(),
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyguardManager,
+ mFlags,
+ mUserTracker);
mMediaOutputController.start(mCb);
@@ -306,11 +328,22 @@
@Test
public void stop_withoutPackageName_verifyMediaControllerDeinit() {
- mMediaOutputController = new MediaOutputController(mSpyContext, null,
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotifCollection, mDialogTransitionAnimator,
- mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
- mKeyguardManager, mFlags, mUserTracker);
+ mMediaOutputController =
+ new MediaOutputController(
+ mSpyContext,
+ null,
+ mSpyContext.getUser(),
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyguardManager,
+ mFlags,
+ mUserTracker);
mMediaOutputController.start(mCb);
@@ -550,12 +583,22 @@
@Test
public void getAppSourceName_packageNameIsNull_returnsNull() {
- MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
- "",
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotifCollection, mDialogTransitionAnimator,
- mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
- mKeyguardManager, mFlags, mUserTracker);
+ MediaOutputController testMediaOutputController =
+ new MediaOutputController(
+ mSpyContext,
+ "",
+ mSpyContext.getUser(),
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyguardManager,
+ mFlags,
+ mUserTracker);
testMediaOutputController.start(mCb);
reset(mCb);
@@ -573,12 +616,22 @@
@Test
public void getNotificationSmallIcon_packageNameIsNull_returnsNull() {
- MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
- "",
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotifCollection, mDialogTransitionAnimator,
- mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
- mKeyguardManager, mFlags, mUserTracker);
+ MediaOutputController testMediaOutputController =
+ new MediaOutputController(
+ mSpyContext,
+ "",
+ mSpyContext.getUser(),
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyguardManager,
+ mFlags,
+ mUserTracker);
testMediaOutputController.start(mCb);
reset(mCb);
@@ -609,12 +662,22 @@
@Test
public void addDeviceToPlayMedia_callsLocalMediaManager() {
- MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
- null,
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotifCollection, mDialogTransitionAnimator,
- mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
- mKeyguardManager, mFlags, mUserTracker);
+ MediaOutputController testMediaOutputController =
+ new MediaOutputController(
+ mSpyContext,
+ null,
+ mSpyContext.getUser(),
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyguardManager,
+ mFlags,
+ mUserTracker);
LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class);
testMediaOutputController.mLocalMediaManager = mockLocalMediaManager;
@@ -625,12 +688,22 @@
@Test
public void removeDeviceFromPlayMedia_callsLocalMediaManager() {
- MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
- null,
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotifCollection, mDialogTransitionAnimator,
- mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
- mKeyguardManager, mFlags, mUserTracker);
+ MediaOutputController testMediaOutputController =
+ new MediaOutputController(
+ mSpyContext,
+ null,
+ mSpyContext.getUser(),
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyguardManager,
+ mFlags,
+ mUserTracker);
LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class);
testMediaOutputController.mLocalMediaManager = mockLocalMediaManager;
@@ -894,11 +967,22 @@
@Test
public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
- mMediaOutputController = new MediaOutputController(mSpyContext, null,
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotifCollection, mDialogTransitionAnimator,
- mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
- mKeyguardManager, mFlags, mUserTracker);
+ mMediaOutputController =
+ new MediaOutputController(
+ mSpyContext,
+ null,
+ mSpyContext.getUser(),
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyguardManager,
+ mFlags,
+ mUserTracker);
assertThat(mMediaOutputController.getNotificationIcon()).isNull();
}
@@ -1085,12 +1169,22 @@
@Test
public void setTemporaryAllowListExceptionIfNeeded_packageNameIsNull_NoAction() {
- MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
- null,
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotifCollection, mDialogTransitionAnimator,
- mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
- mKeyguardManager, mFlags, mUserTracker);
+ MediaOutputController testMediaOutputController =
+ new MediaOutputController(
+ mSpyContext,
+ null,
+ mSpyContext.getUser(),
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyguardManager,
+ mFlags,
+ mUserTracker);
testMediaOutputController.setTemporaryAllowListExceptionIfNeeded(mMediaDevice2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
index 83def8e4..3b6a88a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
@@ -18,6 +18,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -61,7 +62,7 @@
mMediaOutputDialogReceiver.onReceive(getContext(), intent);
verify(mMockMediaOutputDialogManager, times(1))
- .createAndShow(getContext().getPackageName(), false, null);
+ .createAndShow(eq(getContext().getPackageName()), eq(false), any(), any());
verify(mMockMediaOutputBroadcastDialogManager, never())
.createAndShow(any(), anyBoolean(), any());
}
@@ -72,7 +73,8 @@
intent.putExtra("Wrong Package Name Key", getContext().getPackageName());
mMediaOutputDialogReceiver.onReceive(getContext(), intent);
- verify(mMockMediaOutputDialogManager, never()).createAndShow(any(), anyBoolean(), any());
+ verify(mMockMediaOutputDialogManager, never())
+ .createAndShow(any(), anyBoolean(), any(), any());
verify(mMockMediaOutputBroadcastDialogManager, never())
.createAndShow(any(), anyBoolean(), any());
}
@@ -82,7 +84,8 @@
Intent intent = new Intent(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG);
mMediaOutputDialogReceiver.onReceive(getContext(), intent);
- verify(mMockMediaOutputDialogManager, never()).createAndShow(any(), anyBoolean(), any());
+ verify(mMockMediaOutputDialogManager, never())
+ .createAndShow(any(), anyBoolean(), any(), any());
verify(mMockMediaOutputBroadcastDialogManager, never())
.createAndShow(any(), anyBoolean(), any());
}
@@ -95,7 +98,8 @@
intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName());
mMediaOutputDialogReceiver.onReceive(getContext(), intent);
- verify(mMockMediaOutputDialogManager, never()).createAndShow(any(), anyBoolean(), any());
+ verify(mMockMediaOutputDialogManager, never())
+ .createAndShow(any(), anyBoolean(), any(), any());
verify(mMockMediaOutputBroadcastDialogManager, never())
.createAndShow(any(), anyBoolean(), any());
}
@@ -108,9 +112,10 @@
intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName());
mMediaOutputDialogReceiver.onReceive(getContext(), intent);
- verify(mMockMediaOutputDialogManager, never()).createAndShow(any(), anyBoolean(), any());
+ verify(mMockMediaOutputDialogManager, never())
+ .createAndShow(any(), anyBoolean(), any(), any());
verify(mMockMediaOutputBroadcastDialogManager, times(1))
- .createAndShow(getContext().getPackageName(), true, null);
+ .createAndShow(eq(getContext().getPackageName()), eq(true), any());
}
@Test
@@ -121,7 +126,8 @@
intent.putExtra("Wrong Package Name Key", getContext().getPackageName());
mMediaOutputDialogReceiver.onReceive(getContext(), intent);
- verify(mMockMediaOutputDialogManager, never()).createAndShow(any(), anyBoolean(), any());
+ verify(mMockMediaOutputDialogManager, never())
+ .createAndShow(any(), anyBoolean(), any(), any());
verify(mMockMediaOutputBroadcastDialogManager, never())
.createAndShow(any(), anyBoolean(), any());
}
@@ -133,7 +139,8 @@
MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
mMediaOutputDialogReceiver.onReceive(getContext(), intent);
- verify(mMockMediaOutputDialogManager, never()).createAndShow(any(), anyBoolean(), any());
+ verify(mMockMediaOutputDialogManager, never())
+ .createAndShow(any(), anyBoolean(), any(), any());
verify(mMockMediaOutputBroadcastDialogManager, never())
.createAndShow(any(), anyBoolean(), any());
}
@@ -145,7 +152,8 @@
intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName());
mMediaOutputDialogReceiver.onReceive(getContext(), intent);
- verify(mMockMediaOutputDialogManager, never()).createAndShow(any(), anyBoolean(), any());
+ verify(mMockMediaOutputDialogManager, never())
+ .createAndShow(any(), anyBoolean(), any(), any());
verify(mMockMediaOutputBroadcastDialogManager, never())
.createAndShow(any(), anyBoolean(), any());
}
@@ -155,7 +163,8 @@
Intent intent = new Intent("UnKnown Action");
mMediaOutputDialogReceiver.onReceive(getContext(), intent);
- verify(mMockMediaOutputDialogManager, never()).createAndShow(any(), anyBoolean(), any());
+ verify(mMockMediaOutputDialogManager, never())
+ .createAndShow(any(), anyBoolean(), any(), any());
verify(mMockMediaOutputBroadcastDialogManager, never())
.createAndShow(any(), anyBoolean(), any());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 84300da..cdef964 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -137,11 +137,22 @@
Mockito.eq(userHandle))).thenReturn(
mMediaControllers);
- mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotifCollection, mDialogTransitionAnimator,
- mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
- mKeyguardManager, mFlags, mUserTracker);
+ mMediaOutputController =
+ new MediaOutputController(
+ mContext,
+ TEST_PACKAGE,
+ mContext.getUser(),
+ mMediaSessionManager,
+ mLocalBluetoothManager,
+ mStarter,
+ mNotifCollection,
+ mDialogTransitionAnimator,
+ mNearbyMediaDevicesManager,
+ mAudioManager,
+ mPowerExemptionManager,
+ mKeyguardManager,
+ mFlags,
+ mUserTracker);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = makeTestDialog(mMediaOutputController);
mMediaOutputDialog.show();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
new file mode 100644
index 0000000..e044eec
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.mediaprojection.permission
+
+import android.app.AlertDialog
+import android.media.projection.MediaProjectionConfig
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.WindowManager
+import android.widget.Spinner
+import android.widget.TextView
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.AlertDialogWithDelegate
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.mockito.mock
+import junit.framework.Assert.assertEquals
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class MediaProjectionPermissionDialogDelegateTest : SysuiTestCase() {
+
+ private lateinit var dialog: AlertDialog
+
+ private val flags = mock<FeatureFlagsClassic>()
+ private val onStartRecordingClicked = mock<Runnable>()
+ private val mediaProjectionMetricsLogger = mock<MediaProjectionMetricsLogger>()
+
+ private val mediaProjectionConfig: MediaProjectionConfig =
+ MediaProjectionConfig.createConfigForDefaultDisplay()
+ private val appName: String = "testApp"
+ private val hostUid: Int = 12345
+
+ private val resIdSingleApp = R.string.screen_share_permission_dialog_option_single_app
+ private val resIdFullScreen = R.string.screen_share_permission_dialog_option_entire_screen
+ private val resIdSingleAppDisabled =
+ R.string.media_projection_entry_app_permission_dialog_single_app_disabled
+
+ @Before
+ fun setUp() {
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
+ }
+
+ @After
+ fun teardown() {
+ if (::dialog.isInitialized) {
+ dialog.dismiss()
+ }
+ }
+
+ @Test
+ fun showDialog_forceShowPartialScreenShareFalse() {
+ // Set up dialog with MediaProjectionConfig.createConfigForDefaultDisplay() and
+ // overrideDisableSingleAppOption = false
+ val overrideDisableSingleAppOption = false
+ setUpAndShowDialog(overrideDisableSingleAppOption)
+
+ val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
+ val secondOptionText =
+ spinner.adapter
+ .getDropDownView(1, null, spinner)
+ .findViewById<TextView>(android.R.id.text2)
+ ?.text
+
+ // check that the first option is full screen and enabled
+ assertEquals(context.getString(resIdFullScreen), spinner.selectedItem)
+
+ // check that the second option is single app and disabled
+ assertEquals(context.getString(resIdSingleAppDisabled, appName), secondOptionText)
+ }
+
+ @Test
+ fun showDialog_forceShowPartialScreenShareTrue() {
+ // Set up dialog with MediaProjectionConfig.createConfigForDefaultDisplay() and
+ // overrideDisableSingleAppOption = true
+ val overrideDisableSingleAppOption = true
+ setUpAndShowDialog(overrideDisableSingleAppOption)
+
+ val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
+ val secondOptionText =
+ spinner.adapter
+ .getDropDownView(1, null, spinner)
+ .findViewById<TextView>(android.R.id.text1)
+ ?.text
+
+ // check that the first option is single app and enabled
+ assertEquals(context.getString(resIdSingleApp), spinner.selectedItem)
+
+ // check that the second option is full screen and enabled
+ assertEquals(context.getString(resIdFullScreen), secondOptionText)
+ }
+
+ private fun setUpAndShowDialog(overrideDisableSingleAppOption: Boolean) {
+ val delegate =
+ MediaProjectionPermissionDialogDelegate(
+ context,
+ mediaProjectionConfig,
+ {},
+ onStartRecordingClicked,
+ appName,
+ overrideDisableSingleAppOption,
+ hostUid,
+ mediaProjectionMetricsLogger
+ )
+
+ dialog = AlertDialogWithDelegate(context, R.style.Theme_SystemUI_Dialog, delegate)
+ SystemUIDialog.applyFlags(dialog)
+ SystemUIDialog.setDialogSize(dialog)
+
+ dialog.window?.addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS
+ )
+
+ delegate.onCreate(dialog, savedInstanceState = null)
+ dialog.show()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index a702dda..224e755 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -35,6 +35,7 @@
import static org.mockito.Mockito.when;
import android.content.ComponentName;
+import android.content.res.Configuration;
import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -55,6 +56,8 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.FakeConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import dagger.Lazy;
@@ -117,6 +120,7 @@
EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
@Mock
NotificationShadeWindowController mNotificationShadeWindowController;
+ ConfigurationController mConfigurationController = new FakeConfigurationController();
private AccessibilityManager.AccessibilityServicesStateChangeListener
mAccessibilityServicesStateChangeListener;
@@ -144,9 +148,8 @@
mSystemActions, mOverviewProxyService, mAssistManagerLazy,
() -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class),
mNavigationModeController, mEdgeBackGestureHandlerFactory, mWm, mUserTracker,
- mDisplayTracker, mNotificationShadeWindowController, mDumpManager, mCommandQueue,
- mSynchronousExecutor);
-
+ mDisplayTracker, mNotificationShadeWindowController, mConfigurationController,
+ mDumpManager, mCommandQueue, mSynchronousExecutor);
}
@Test
@@ -335,6 +338,12 @@
assertThat(state2.mWindowState).isNotEqualTo(newState);
}
+ @Test
+ public void configUpdatePropagatesToEdgeBackGestureHandler() {
+ mConfigurationController.onConfigurationChanged(Configuration.EMPTY);
+ verify(mEdgeBackGestureHandler, times(1)).onConfigurationChanged(any());
+ }
+
private List<String> createFakeShortcutTargets() {
return new ArrayList<>(List.of("a", "b", "c", "d"));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index d405df7..354a87a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -37,11 +37,8 @@
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;
-import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import android.util.SparseArray;
@@ -293,23 +290,6 @@
}
@Test
- public void testConfigurationChange_taskbarNotInitialized() {
- Configuration configuration = mContext.getResources().getConfiguration();
- mNavigationBarController.mIsLargeScreen = true;
- mNavigationBarController.onConfigChanged(configuration);
- verify(mTaskbarDelegate, never()).onConfigurationChanged(configuration);
- }
-
- @Test
- public void testConfigurationChange_taskbarInitialized() {
- Configuration configuration = mContext.getResources().getConfiguration();
- mNavigationBarController.mIsLargeScreen = true;
- when(mTaskbarDelegate.isInitialized()).thenReturn(true);
- mNavigationBarController.onConfigChanged(configuration);
- verify(mTaskbarDelegate, times(1)).onConfigurationChanged(configuration);
- }
-
- @Test
public void testShouldRenderTaskbar_taskbarNotRenderedOnPhone() {
mNavigationBarController.mIsLargeScreen = false;
mNavigationBarController.mIsPhone = true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index b38d5e3..0e7a215 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -111,6 +111,7 @@
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.DeviceConfigProxyFake;
@@ -275,8 +276,8 @@
mKeyguardStateController, mock(NavigationModeController.class),
mEdgeBackGestureHandlerFactory, mock(IWindowManager.class),
mock(UserTracker.class), mock(DisplayTracker.class),
- mNotificationShadeWindowController, mock(DumpManager.class),
- mock(CommandQueue.class), mSynchronousExecutor));
+ mNotificationShadeWindowController, mock(ConfigurationController.class),
+ mock(DumpManager.class), mock(CommandQueue.class), mSynchronousExecutor));
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
});
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index e4a4836..6956bea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -57,6 +57,7 @@
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.EnableSceneContainer;
import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.qs.customize.QSCustomizerController;
@@ -66,7 +67,6 @@
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.CommandQueue;
@@ -115,7 +115,6 @@
@Mock private FooterActionsViewBinder mFooterActionsViewBinder;
@Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
@Mock private FeatureFlagsClassic mFeatureFlags;
- @Mock private SceneContainerFlags mSceneContainerFlags;
private ViewGroup mQsView;
private final CommandQueue mCommandQueue =
@@ -127,7 +126,6 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- when(mSceneContainerFlags.isEnabled()).thenReturn(false);
mUnderTest = instantiate();
@@ -496,8 +494,8 @@
}
@Test
+ @EnableSceneContainer
public void testSceneContainerFlagsEnabled_FooterActionsRemoved_controllerNotStarted() {
- when(mSceneContainerFlags.isEnabled()).thenReturn(true);
clearInvocations(
mFooterActionsViewBinder, mFooterActionsViewModel, mFooterActionsViewModelFactory);
QSImpl other = instantiate();
@@ -513,9 +511,8 @@
}
@Test
+ @EnableSceneContainer
public void testSceneContainerFlagsEnabled_statusBarStateIsShade() {
- when(mSceneContainerFlags.isEnabled()).thenReturn(true);
-
mUnderTest.onStateChanged(KEYGUARD);
assertThat(mUnderTest.getStatusBarState()).isEqualTo(SHADE);
@@ -524,9 +521,8 @@
}
@Test
+ @EnableSceneContainer
public void testSceneContainerFlagsEnabled_isKeyguardState_alwaysFalse() {
- when(mSceneContainerFlags.isEnabled()).thenReturn(true);
-
mUnderTest.onStateChanged(KEYGUARD);
assertThat(mUnderTest.isKeyguardState()).isFalse();
@@ -559,8 +555,8 @@
mFooterActionsViewModelFactory,
mFooterActionsViewBinder,
mLargeScreenShadeInterpolator,
- mFeatureFlags,
- mSceneContainerFlags);
+ mFeatureFlags
+ );
}
private void setUpOther() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 0101741..542bfaa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs;
+import static com.android.systemui.Flags.FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -33,6 +35,8 @@
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.ContextThemeWrapper;
@@ -45,13 +49,14 @@
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.haptics.qs.QSLongPressEffect;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
-import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.util.animation.DisappearParameters;
@@ -66,11 +71,14 @@
import java.util.Collections;
import java.util.List;
+import javax.inject.Provider;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
public class QSPanelControllerBaseTest extends SysuiTestCase {
+ private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
@Mock
private QSPanel mQSPanel;
@Mock
@@ -101,8 +109,8 @@
Configuration mConfiguration;
@Mock
Runnable mHorizontalLayoutListener;
- @Mock
- VibratorHelper mVibratorHelper;
+ private TestableLongPressEffectProvider mLongPressEffectProvider =
+ new TestableLongPressEffectProvider();
private QSPanelControllerBase<QSPanel> mController;
@@ -114,7 +122,7 @@
DumpManager dumpManager) {
super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger,
qsLogger, dumpManager, new ResourcesSplitShadeStateController(),
- mVibratorHelper);
+ mLongPressEffectProvider);
}
@Override
@@ -123,6 +131,17 @@
}
}
+ private class TestableLongPressEffectProvider implements Provider<QSLongPressEffect> {
+
+ private int mEffectsProvided = 0;
+
+ @Override
+ public QSLongPressEffect get() {
+ mEffectsProvided++;
+ return mKosmos.getQsLongPressEffect();
+ }
+ }
+
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -421,6 +440,27 @@
}
@Test
+ @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
+ public void setTiles_longPressEffectEnabled_nonNullLongPressEffectsAreProvided() {
+ mLongPressEffectProvider.mEffectsProvided = 0;
+ when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
+ mController.setTiles();
+
+ // There is one non-null effect provided for each tile in the host
+ assertThat(mLongPressEffectProvider.mEffectsProvided).isEqualTo(2);
+ }
+
+ @Test
+ @DisableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
+ public void setTiles_longPressEffectDisabled_noLongPressEffectsAreProvided() {
+ mLongPressEffectProvider.mEffectsProvided = 0;
+ when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
+ mController.setTiles();
+
+ assertThat(mLongPressEffectProvider.mEffectsProvided).isEqualTo(0);
+ }
+
+ @Test
public void setTiles_differentTiles_extraTileRemoved() {
when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
mController.setTiles();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 916e8dd..0275643 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -7,23 +7,23 @@
import android.view.ContextThemeWrapper
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.haptics.qs.QSLongPressEffect
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.customize.QSCustomizerController
import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.res.R
import com.android.systemui.settings.brightness.BrightnessController
import com.android.systemui.settings.brightness.BrightnessSliderController
-import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.tuner.TunerService
import com.google.common.truth.Truth.assertThat
+import javax.inject.Provider
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -62,9 +62,7 @@
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var configuration: Configuration
@Mock private lateinit var pagedTileLayout: PagedTileLayout
- @Mock private lateinit var vibratorHelper: VibratorHelper
-
- private val sceneContainerFlags = FakeSceneContainerFlags()
+ @Mock private lateinit var longPressEffectProvider: Provider<QSLongPressEffect>
private lateinit var controller: QSPanelController
private val testableResources: TestableResources = mContext.orCreateTestableResources
@@ -102,8 +100,7 @@
falsingManager,
statusBarKeyguardViewManager,
ResourcesSplitShadeStateController(),
- sceneContainerFlags,
- vibratorHelper,
+ longPressEffectProvider,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 71a9a8b..1eb0a51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -22,15 +22,15 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.haptics.qs.QSLongPressEffect
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.customize.QSCustomizerController
import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.util.leak.RotationUtils
import org.junit.After
@@ -45,6 +45,7 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import javax.inject.Provider
import org.mockito.Mockito.`when` as whenever
@SmallTest
@@ -60,7 +61,7 @@
@Mock private lateinit var tile: QSTile
@Mock private lateinit var tileLayout: TileLayout
@Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
- @Mock private lateinit var vibratorHelper: VibratorHelper
+ @Mock private lateinit var longPressEffectProvider: Provider<QSLongPressEffect>
private val uiEventLogger = UiEventLoggerFake()
private val dumpManager = DumpManager()
@@ -92,7 +93,7 @@
uiEventLogger,
qsLogger,
dumpManager,
- vibratorHelper,
+ longPressEffectProvider,
)
controller.init()
@@ -161,7 +162,7 @@
uiEventLogger: UiEventLoggerFake,
qsLogger: QSLogger,
dumpManager: DumpManager,
- vibratorHelper: VibratorHelper,
+ longPressEffectProvider: Provider<QSLongPressEffect>,
) :
QuickQSPanelController(
view,
@@ -175,7 +176,7 @@
qsLogger,
dumpManager,
ResourcesSplitShadeStateController(),
- vibratorHelper,
+ longPressEffectProvider,
) {
private var rotation = RotationUtils.ROTATION_NONE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 8acde36..4915e55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -20,15 +20,14 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -43,8 +42,6 @@
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var context: Context
- private val sceneContainerFlags = FakeSceneContainerFlags()
-
private lateinit var controller: QuickStatusBarHeaderController
@Before
@@ -55,9 +52,8 @@
`when`(view.context).thenReturn(context)
controller = QuickStatusBarHeaderController(
- view,
- quickQSPanelController,
- sceneContainerFlags,
+ view,
+ quickQSPanelController,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index 2b1ac91..512ca53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -18,7 +18,6 @@
import android.content.Context
import android.graphics.drawable.Drawable
-import android.platform.test.annotations.EnableFlags
import android.service.quicksettings.Tile
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -28,10 +27,12 @@
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.TextView
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.haptics.qs.QSLongPressEffect
+import com.android.systemui.haptics.qs.qsLongPressEffect
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -50,13 +51,14 @@
private lateinit var tileView: FakeTileView
private lateinit var customDrawableView: View
private lateinit var chevronView: View
+ private val kosmos = testKosmos()
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
context.ensureTestableResources()
- tileView = FakeTileView(context, false)
+ tileView = FakeTileView(context, false, kosmos.qsLongPressEffect)
customDrawableView = tileView.requireViewById(R.id.customDrawable)
chevronView = tileView.requireViewById(R.id.chevron)
}
@@ -383,7 +385,6 @@
}
@Test
- @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
fun onStateChange_longPressEffectActive_withInvalidDuration_doesNotCreateEffect() {
val state = QSTile.State() // A state that handles longPress
@@ -393,12 +394,11 @@
// WHEN the state changes
tileView.changeState(state)
- // THEN the long-press effect is not created
- assertThat(tileView.hasLongPressEffect).isFalse()
+ // THEN the long-press effect is not initialized
+ assertThat(tileView.isLongPressEffectInitialized).isFalse()
}
@Test
- @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
fun onStateChange_longPressEffectActive_withValidDuration_createsEffect() {
// GIVEN a test state that handles long-press and a valid long-press effect duration
val state = QSTile.State()
@@ -406,12 +406,11 @@
// WHEN the state changes
tileView.changeState(state)
- // THEN the long-press effect created
- assertThat(tileView.hasLongPressEffect).isTrue()
+ // THEN the long-press effect is initialized
+ assertThat(tileView.isLongPressEffectInitialized).isTrue()
}
@Test
- @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
fun onStateChange_fromLongPress_to_noLongPress_unBoundsTile() {
// GIVEN a state that no longer handles long-press
val state = QSTile.State()
@@ -421,11 +420,10 @@
tileView.changeState(state)
// THEN the view binder no longer binds the view to the long-press effect
- assertThat(tileView.isLongPressEffectBound).isFalse()
+ assertThat(tileView.longPressEffectHandle).isNull()
}
@Test
- @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
fun onStateChange_fromNoLongPress_to_longPress_bindsTile() {
// GIVEN that the tile has changed to a state that does not handle long-press
val state = QSTile.State()
@@ -437,15 +435,53 @@
tileView.changeState(state)
// THEN the view is bounded to the long-press effect
- assertThat(tileView.isLongPressEffectBound).isTrue()
+ assertThat(tileView.longPressEffectHandle).isNotNull()
+ }
+
+ @Test
+ fun onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverBindsEffect() {
+ // GIVEN a tile where the long-press effect is null
+ tileView = FakeTileView(context, false, null)
+
+ // GIVEN a state that no longer handles long-press
+ val state = QSTile.State()
+ state.handlesLongClick = false
+
+ // WHEN the state changes
+ tileView.changeState(state)
+
+ // THEN the view binder does not bind the view and no effect is initialized
+ assertThat(tileView.longPressEffectHandle).isNull()
+ assertThat(tileView.isLongPressEffectInitialized).isFalse()
+ }
+
+ @Test
+ fun onStateChange_withoutLongPressEffect_fromNoLongPress_to_longPress_neverBindsEffect() {
+ // GIVEN a tile where the long-press effect is null
+ tileView = FakeTileView(context, false, null)
+
+ // GIVEN that the tile has changed to a state that does not handle long-press
+ val state = QSTile.State()
+ state.handlesLongClick = false
+ tileView.changeState(state)
+
+ // WHEN the state changes back to handling long-press
+ state.handlesLongClick = true
+ tileView.changeState(state)
+
+ // THEN the view binder does not bind the view and no effect is initialized
+ assertThat(tileView.longPressEffectHandle).isNull()
+ assertThat(tileView.isLongPressEffectInitialized).isFalse()
}
class FakeTileView(
context: Context,
- collapsed: Boolean
+ collapsed: Boolean,
+ longPressEffect: QSLongPressEffect?,
) : QSTileViewImpl(
ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings),
- collapsed
+ collapsed,
+ longPressEffect,
) {
var constantLongPressEffectDuration = 500
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index 1313227..387f27d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -42,7 +42,6 @@
import com.android.systemui.navigationbar.NavigationBarController
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeViewController
@@ -249,7 +248,6 @@
sysuiUnlockAnimationController,
inWindowLauncherUnlockAnimationManager,
assistUtils,
- FakeSceneContainerFlags(),
dumpManager,
unfoldTransitionProgressForwarder,
broadcastDispatcher
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
index 5e7d8fb..91f3912 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
+import kotlin.test.Ignore
import kotlin.test.Test
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScheduler
@@ -55,6 +56,7 @@
private lateinit var actionExecutor: ActionExecutor
+ @Ignore // Fixed with newer mockito version (in main)
@Test
fun startSharedTransition_callsLaunchIntent() = runTest {
actionExecutor = createActionExecutor()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
index bde821b..853e50a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
@@ -16,8 +16,6 @@
package com.android.systemui.screenshot
-import android.app.Notification
-import android.app.PendingIntent
import android.content.Intent
import android.net.Uri
import android.os.Process
@@ -27,8 +25,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
-import com.android.systemui.clipboardoverlay.EditTextActivity
-import com.android.systemui.res.R
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
@@ -41,10 +37,7 @@
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.kotlin.any
-import org.mockito.kotlin.never
import org.mockito.kotlin.verify
-import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -52,7 +45,6 @@
private val actionExecutor = mock<ActionExecutor>()
private val accessibilityManager = mock<AccessibilityManager>()
private val uiEventLogger = mock<UiEventLogger>()
- private val smartActionsProvider = mock<SmartActionsProvider>()
private val request = ScreenshotData.forTesting()
private val validResult = ScreenshotSavedResult(Uri.EMPTY, Process.myUserHandle(), 0)
@@ -119,42 +111,10 @@
assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_CHOOSER)
}
- @Test
- fun quickShareTapped_wrapsAndSendsIntent() = runTest {
- val quickShare =
- Notification.Action(
- R.drawable.ic_screenshot_edit,
- "TestQuickShare",
- PendingIntent.getActivity(
- context,
- 0,
- Intent(context, EditTextActivity::class.java),
- PendingIntent.FLAG_MUTABLE
- )
- )
- whenever(smartActionsProvider.requestQuickShare(any(), any(), any())).then {
- (it.getArgument(2) as ((Notification.Action) -> Unit)).invoke(quickShare)
- }
- whenever(smartActionsProvider.wrapIntent(any(), any(), any(), any())).thenAnswer {
- (it.getArgument(0) as Notification.Action).actionIntent
- }
- actionsProvider = createActionsProvider()
-
- viewModel.actions.value[2].onClicked?.invoke()
- verify(uiEventLogger, never())
- .log(eq(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED), any(), any())
- verify(smartActionsProvider, never()).wrapIntent(any(), any(), any(), any())
- actionsProvider.setCompletedScreenshot(validResult)
- verify(smartActionsProvider)
- .wrapIntent(eq(quickShare), eq(validResult.uri), eq(validResult.subject), eq("testid"))
- verify(uiEventLogger).log(eq(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED), eq(0), eq(""))
- }
-
private fun createActionsProvider(): ScreenshotActionsProvider {
return DefaultScreenshotActionsProvider(
context,
viewModel,
- smartActionsProvider,
uiEventLogger,
request,
"testid",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
index 587da2d..3756ec1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
@@ -18,11 +18,6 @@
import android.app.ActivityTaskManager.RootTaskInfo
import android.app.IActivityTaskManager
-import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
-import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
-import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
-import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
-import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
import android.content.ComponentName
import android.content.Context
import android.graphics.Rect
@@ -31,6 +26,12 @@
import android.testing.AndroidTestingRunner
import com.android.systemui.SysuiTestCase
import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
+import com.android.systemui.screenshot.policy.ActivityType.Home
+import com.android.systemui.screenshot.policy.ActivityType.Undefined
+import com.android.systemui.screenshot.policy.WindowingMode.FullScreen
+import com.android.systemui.screenshot.policy.WindowingMode.PictureInPicture
+import com.android.systemui.screenshot.policy.newChildTask
+import com.android.systemui.screenshot.policy.newRootTaskInfo
import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -58,20 +59,19 @@
),
Rect(0, 0, 1080, 2400),
UserHandle.of(MANAGED_PROFILE_USER),
- 65))
+ 65
+ )
+ )
}
@Test
fun findPrimaryContent_ignoresPipTask() = runBlocking {
- val policy = fakeTasksPolicyImpl(
- mContext,
- shadeExpanded = false,
- tasks = listOf(
- pipTask,
- fullScreenWorkProfileTask,
- launcherTask,
- emptyTask)
- )
+ val policy =
+ fakeTasksPolicyImpl(
+ mContext,
+ shadeExpanded = false,
+ tasks = listOf(pipTask, fullScreenWorkProfileTask, launcherTask, emptyTask)
+ )
val info = policy.findPrimaryContent(DISPLAY_ID)
assertThat(info).isEqualTo(fullScreenWorkProfileTask.toDisplayContentInfo())
@@ -79,14 +79,12 @@
@Test
fun findPrimaryContent_shadeExpanded_ignoresTopTask() = runBlocking {
- val policy = fakeTasksPolicyImpl(
- mContext,
- shadeExpanded = true,
- tasks = listOf(
- fullScreenWorkProfileTask,
- launcherTask,
- emptyTask)
- )
+ val policy =
+ fakeTasksPolicyImpl(
+ mContext,
+ shadeExpanded = true,
+ tasks = listOf(fullScreenWorkProfileTask, launcherTask, emptyTask)
+ )
val info = policy.findPrimaryContent(DISPLAY_ID)
assertThat(info).isEqualTo(policy.systemUiContent)
@@ -94,11 +92,7 @@
@Test
fun findPrimaryContent_emptyTaskList() = runBlocking {
- val policy = fakeTasksPolicyImpl(
- mContext,
- shadeExpanded = false,
- tasks = listOf()
- )
+ val policy = fakeTasksPolicyImpl(mContext, shadeExpanded = false, tasks = listOf())
val info = policy.findPrimaryContent(DISPLAY_ID)
assertThat(info).isEqualTo(policy.systemUiContent)
@@ -106,14 +100,12 @@
@Test
fun findPrimaryContent_workProfileNotOnTop() = runBlocking {
- val policy = fakeTasksPolicyImpl(
- mContext,
- shadeExpanded = false,
- tasks = listOf(
- launcherTask,
- fullScreenWorkProfileTask,
- emptyTask)
- )
+ val policy =
+ fakeTasksPolicyImpl(
+ mContext,
+ shadeExpanded = false,
+ tasks = listOf(launcherTask, fullScreenWorkProfileTask, emptyTask)
+ )
val info = policy.findPrimaryContent(DISPLAY_ID)
assertThat(info).isEqualTo(launcherTask.toDisplayContentInfo())
@@ -129,102 +121,80 @@
val dispatcher = Dispatchers.Unconfined
val displayTracker = FakeDisplayTracker(context)
- return object : ScreenshotPolicyImpl(context, userManager, atmService, dispatcher,
- displayTracker) {
+ return object :
+ ScreenshotPolicyImpl(context, userManager, atmService, dispatcher, displayTracker) {
override suspend fun isManagedProfile(userId: Int) = (userId == MANAGED_PROFILE_USER)
override suspend fun getAllRootTaskInfosOnDisplay(displayId: Int) = tasks
override suspend fun isNotificationShadeExpanded() = shadeExpanded
}
}
- private val pipTask = RootTaskInfo().apply {
- configuration.windowConfiguration.apply {
- windowingMode = WINDOWING_MODE_PINNED
- setBounds(Rect(628, 1885, 1038, 2295))
- activityType = ACTIVITY_TYPE_STANDARD
+ private val pipTask =
+ newRootTaskInfo(
+ taskId = 66,
+ userId = PRIMARY_USER,
+ displayId = DISPLAY_ID,
+ bounds = Rect(628, 1885, 1038, 2295),
+ windowingMode = PictureInPicture,
+ topActivity = ComponentName.unflattenFromString(YOUTUBE_PIP_ACTIVITY),
+ ) {
+ listOf(newChildTask(taskId = 66, userId = 0, name = YOUTUBE_HOME_ACTIVITY))
}
- displayId = DISPLAY_ID
- userId = PRIMARY_USER
- taskId = 66
- visible = true
- isVisible = true
- isRunning = true
- numActivities = 1
- topActivity = ComponentName(
- "com.google.android.youtube",
- "com.google.android.apps.youtube.app.watchwhile.WatchWhileActivity"
- )
- childTaskIds = intArrayOf(66)
- childTaskNames = arrayOf("com.google.android.youtube/" +
- "com.google.android.youtube.app.honeycomb.Shell\$HomeActivity")
- childTaskUserIds = intArrayOf(0)
- childTaskBounds = arrayOf(Rect(628, 1885, 1038, 2295))
- }
- private val fullScreenWorkProfileTask = RootTaskInfo().apply {
- configuration.windowConfiguration.apply {
- windowingMode = WINDOWING_MODE_FULLSCREEN
- setBounds(Rect(0, 0, 1080, 2400))
- activityType = ACTIVITY_TYPE_STANDARD
+ private val fullScreenWorkProfileTask =
+ newRootTaskInfo(
+ taskId = 65,
+ userId = MANAGED_PROFILE_USER,
+ displayId = DISPLAY_ID,
+ bounds = Rect(0, 0, 1080, 2400),
+ windowingMode = FullScreen,
+ topActivity = ComponentName.unflattenFromString(FILES_HOME_ACTIVITY),
+ ) {
+ listOf(
+ newChildTask(taskId = 65, userId = MANAGED_PROFILE_USER, name = FILES_HOME_ACTIVITY)
+ )
}
- displayId = DISPLAY_ID
- userId = MANAGED_PROFILE_USER
- taskId = 65
- visible = true
- isVisible = true
- isRunning = true
- numActivities = 1
- topActivity = ComponentName(
- "com.google.android.apps.nbu.files",
- "com.google.android.apps.nbu.files.home.HomeActivity"
- )
- childTaskIds = intArrayOf(65)
- childTaskNames = arrayOf("com.google.android.apps.nbu.files/" +
- "com.google.android.apps.nbu.files.home.HomeActivity")
- childTaskUserIds = intArrayOf(MANAGED_PROFILE_USER)
- childTaskBounds = arrayOf(Rect(0, 0, 1080, 2400))
- }
+ private val launcherTask =
+ newRootTaskInfo(
+ taskId = 1,
+ userId = PRIMARY_USER,
+ displayId = DISPLAY_ID,
+ activityType = Home,
+ windowingMode = FullScreen,
+ bounds = Rect(0, 0, 1080, 2400),
+ topActivity = ComponentName.unflattenFromString(LAUNCHER_ACTIVITY),
+ ) {
+ listOf(newChildTask(taskId = 1, userId = 0, name = LAUNCHER_ACTIVITY))
+ }
- private val launcherTask = RootTaskInfo().apply {
- configuration.windowConfiguration.apply {
- windowingMode = WINDOWING_MODE_FULLSCREEN
- setBounds(Rect(0, 0, 1080, 2400))
- activityType = ACTIVITY_TYPE_HOME
+ private val emptyTask =
+ newRootTaskInfo(
+ taskId = 2,
+ userId = PRIMARY_USER,
+ displayId = DISPLAY_ID,
+ visible = false,
+ running = false,
+ numActivities = 0,
+ activityType = Undefined,
+ bounds = Rect(0, 0, 1080, 2400),
+ ) {
+ listOf(
+ newChildTask(taskId = 3, name = ""),
+ newChildTask(taskId = 4, name = ""),
+ )
}
- displayId = DISPLAY_ID
- taskId = 1
- userId = PRIMARY_USER
- visible = true
- isVisible = true
- isRunning = true
- numActivities = 1
- topActivity = ComponentName(
- "com.google.android.apps.nexuslauncher",
- "com.google.android.apps.nexuslauncher.NexusLauncherActivity",
- )
- childTaskIds = intArrayOf(1)
- childTaskNames = arrayOf("com.google.android.apps.nexuslauncher/" +
- "com.google.android.apps.nexuslauncher.NexusLauncherActivity")
- childTaskUserIds = intArrayOf(0)
- childTaskBounds = arrayOf(Rect(0, 0, 1080, 2400))
- }
-
- private val emptyTask = RootTaskInfo().apply {
- configuration.windowConfiguration.apply {
- windowingMode = WINDOWING_MODE_FULLSCREEN
- setBounds(Rect(0, 0, 1080, 2400))
- activityType = ACTIVITY_TYPE_UNDEFINED
- }
- displayId = DISPLAY_ID
- taskId = 2
- userId = PRIMARY_USER
- visible = false
- isVisible = false
- isRunning = false
- numActivities = 0
- childTaskIds = intArrayOf(3, 4)
- childTaskNames = arrayOf("", "")
- childTaskUserIds = intArrayOf(0, 0)
- childTaskBounds = arrayOf(Rect(0, 0, 1080, 2400), Rect(0, 2400, 1080, 4800))
- }
}
+
+private const val YOUTUBE_HOME_ACTIVITY =
+ "com.google.android.youtube/" + "com.google.android.youtube.app.honeycomb.Shell\$HomeActivity"
+
+private const val FILES_HOME_ACTIVITY =
+ "com.google.android.apps.nbu.files/" + "com.google.android.apps.nbu.files.home.HomeActivity"
+
+private const val YOUTUBE_PIP_ACTIVITY =
+ "com.google.android.youtube/" +
+ "com.google.android.apps.youtube.app.watchwhile.WatchWhileActivity"
+
+private const val LAUNCHER_ACTIVITY =
+ "com.google.android.apps.nexuslauncher/" +
+ "com.google.android.apps.nexuslauncher.NexusLauncherActivity"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt
new file mode 100644
index 0000000..254f1e1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot.data.model
+
+import android.content.ComponentName
+import android.graphics.Rect
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FREE_FORM
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FULL_SCREEN
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.PIP
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.SPLIT_BOTTOM
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.SPLIT_TOP
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks.emptyRootSplit
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks.freeForm
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks.fullScreen
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks.launcher
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks.pictureInPicture
+import com.android.systemui.screenshot.policy.ActivityType
+import com.android.systemui.screenshot.policy.TestUserIds
+import com.android.systemui.screenshot.policy.WindowingMode
+import com.android.systemui.screenshot.policy.newChildTask
+import com.android.systemui.screenshot.policy.newRootTaskInfo
+
+/** Tools for creating a [DisplayContentModel] for different usage scenarios. */
+object DisplayContentScenarios {
+
+ data class TaskSpec(val taskId: Int, val userId: Int, val name: String)
+
+ /** Home screen, with only the launcher visible */
+ fun launcherOnly(shadeExpanded: Boolean = false) =
+ DisplayContentModel(
+ displayId = 0,
+ systemUiState = SystemUiState(shadeExpanded = shadeExpanded),
+ rootTasks =
+ listOf(
+ launcher(visible = true),
+ emptyRootSplit,
+ )
+ )
+
+ /** A Full screen activity for the personal (primary) user, with launcher behind it */
+ fun singleFullScreen(spec: TaskSpec, shadeExpanded: Boolean = false) =
+ DisplayContentModel(
+ displayId = 0,
+ systemUiState = SystemUiState(shadeExpanded = shadeExpanded),
+ rootTasks =
+ listOf(
+ fullScreen(spec, visible = true),
+ launcher(visible = false),
+ emptyRootSplit,
+ )
+ )
+
+ fun splitScreenApps(
+ top: TaskSpec,
+ bottom: TaskSpec,
+ focusedTaskId: Int,
+ shadeExpanded: Boolean = false,
+ ): DisplayContentModel {
+ val topBounds = SPLIT_TOP
+ val bottomBounds = SPLIT_BOTTOM
+ return DisplayContentModel(
+ displayId = 0,
+ systemUiState = SystemUiState(shadeExpanded = shadeExpanded),
+ rootTasks =
+ listOf(
+ newRootTaskInfo(
+ taskId = 2,
+ userId = TestUserIds.PERSONAL,
+ bounds = FULL_SCREEN,
+ topActivity =
+ ComponentName.unflattenFromString(
+ if (top.taskId == focusedTaskId) top.name else bottom.name
+ ),
+ ) {
+ listOf(
+ newChildTask(
+ taskId = top.taskId,
+ bounds = topBounds,
+ userId = top.userId,
+ name = top.name
+ ),
+ newChildTask(
+ taskId = bottom.taskId,
+ bounds = bottomBounds,
+ userId = bottom.userId,
+ name = bottom.name
+ )
+ )
+ // Child tasks are ordered bottom-up in RootTaskInfo.
+ // Sort 'focusedTaskId' last.
+ // Boolean natural ordering: [false, true].
+ .sortedBy { it.id == focusedTaskId }
+ },
+ launcher(visible = false),
+ )
+ )
+ }
+
+ fun pictureInPictureApp(
+ pip: TaskSpec,
+ fullScreen: TaskSpec? = null,
+ shadeExpanded: Boolean = false,
+ ): DisplayContentModel {
+ return DisplayContentModel(
+ displayId = 0,
+ systemUiState = SystemUiState(shadeExpanded = shadeExpanded),
+ rootTasks =
+ buildList {
+ add(pictureInPicture(pip))
+ fullScreen?.also { add(fullScreen(it, visible = true)) }
+ add(launcher(visible = (fullScreen == null)))
+ add(emptyRootSplit)
+ }
+ )
+ }
+
+ fun freeFormApps(
+ vararg tasks: TaskSpec,
+ focusedTaskId: Int,
+ shadeExpanded: Boolean = false,
+ ): DisplayContentModel {
+ val freeFormTasks =
+ tasks
+ .map { freeForm(it) }
+ // Root tasks are ordered top-down in List<RootTaskInfo>.
+ // Sort 'focusedTaskId' last (Boolean natural ordering: [false, true])
+ .sortedBy { it.childTaskIds[0] != focusedTaskId }
+ return DisplayContentModel(
+ displayId = 0,
+ systemUiState = SystemUiState(shadeExpanded = shadeExpanded),
+ rootTasks = freeFormTasks + launcher(visible = true) + emptyRootSplit
+ )
+ }
+
+ /**
+ * All of these are arbitrary dimensions exposed for asserting equality on test data.
+ *
+ * They should not be updated nor compared with any real device usage, except to keep them
+ * somewhat sensible in terms of logical position (Re: PIP, SPLIT, etc).
+ */
+ object Bounds {
+ val FULL_SCREEN = Rect(0, 0, 1080, 2400)
+ val PIP = Rect(440, 1458, 1038, 1794)
+ val SPLIT_TOP = Rect(0, 0, 1080, 1187)
+ val SPLIT_BOTTOM = Rect(0, 1213, 1080, 2400)
+ val FREE_FORM = Rect(119, 332, 1000, 1367)
+ }
+
+ /** A collection of task names used in test scenarios */
+ object ActivityNames {
+ /** The main YouTube activity */
+ const val YOUTUBE =
+ "com.google.android.youtube/" +
+ "com.google.android.youtube.app.honeycomb.Shell\$HomeActivity"
+
+ /** The main Files Activity */
+ const val FILES =
+ "com.google.android.apps.nbu.files/" +
+ "com.google.android.apps.nbu.files.home.HomeActivity"
+
+ /** The YouTube picture-in-picture activity */
+ const val YOUTUBE_PIP =
+ "com.google.android.youtube/" +
+ "com.google.android.apps.youtube.app.watchwhile.WatchWhileActivity"
+
+ /** The NexusLauncher activity */
+ const val LAUNCHER =
+ "com.google.android.apps.nexuslauncher/" +
+ "com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+ }
+
+ /**
+ * A set of predefined RootTaskInfo used in test scenarios, matching as closely as possible
+ * actual values returned by ActivityTaskManager
+ */
+ object RootTasks {
+ /** An empty RootTaskInfo with no child tasks. */
+ val emptyWithNoChildTasks =
+ newRootTaskInfo(
+ taskId = 2,
+ visible = true,
+ running = true,
+ numActivities = 0,
+ bounds = FULL_SCREEN,
+ ) {
+ emptyList()
+ }
+
+ /**
+ * The empty RootTaskInfo that is always at the end of a list from ActivityTaskManager when
+ * no other visible activities are in split mode
+ */
+ val emptyRootSplit =
+ newRootTaskInfo(
+ taskId = 2,
+ visible = false,
+ running = false,
+ numActivities = 0,
+ bounds = FULL_SCREEN,
+ activityType = ActivityType.Undefined,
+ ) {
+ listOf(
+ newChildTask(taskId = 3, bounds = FULL_SCREEN, name = ""),
+ newChildTask(taskId = 4, bounds = Rect(0, 2400, 1080, 3600), name = ""),
+ )
+ }
+
+ /** NexusLauncher on the default display. Usually below all other visible tasks */
+ fun launcher(visible: Boolean) =
+ newRootTaskInfo(
+ taskId = 1,
+ activityType = ActivityType.Home,
+ visible = visible,
+ bounds = FULL_SCREEN,
+ topActivity = ComponentName.unflattenFromString(ActivityNames.LAUNCHER),
+ topActivityType = ActivityType.Home,
+ ) {
+ listOf(newChildTask(taskId = 1002, name = ActivityNames.LAUNCHER))
+ }
+
+ /** A full screen Activity */
+ fun fullScreen(task: TaskSpec, visible: Boolean) =
+ newRootTaskInfo(
+ taskId = task.taskId,
+ userId = task.userId,
+ visible = visible,
+ bounds = FULL_SCREEN,
+ topActivity = ComponentName.unflattenFromString(task.name),
+ ) {
+ listOf(newChildTask(taskId = task.taskId, userId = task.userId, name = task.name))
+ }
+
+ /** An activity in Picture-in-Picture mode */
+ fun pictureInPicture(task: TaskSpec) =
+ newRootTaskInfo(
+ taskId = task.taskId,
+ userId = task.userId,
+ bounds = PIP,
+ windowingMode = WindowingMode.PictureInPicture,
+ topActivity = ComponentName.unflattenFromString(task.name),
+ ) {
+ listOf(newChildTask(taskId = task.taskId, userId = userId, name = task.name))
+ }
+
+ /** An activity in FreeForm mode */
+ fun freeForm(task: TaskSpec) =
+ newRootTaskInfo(
+ taskId = task.taskId,
+ userId = task.userId,
+ bounds = FREE_FORM,
+ windowingMode = WindowingMode.Freeform,
+ topActivity = ComponentName.unflattenFromString(task.name),
+ ) {
+ listOf(newChildTask(taskId = task.taskId, userId = userId, name = task.name))
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/data/repository/ProfileTypeRepositoryKosmos.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/data/repository/ProfileTypeRepositoryKosmos.kt
new file mode 100644
index 0000000..9d2b56a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/data/repository/ProfileTypeRepositoryKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.screenshot.data.model.ProfileType
+import com.android.systemui.screenshot.policy.TestUserIds
+
+val Kosmos.profileTypeRepository by
+ Kosmos.Fixture {
+ ProfileTypeRepository { userId ->
+ when (userId) {
+ TestUserIds.WORK -> ProfileType.WORK
+ TestUserIds.CLONE -> ProfileType.CLONE
+ TestUserIds.PRIVATE -> ProfileType.PRIVATE
+ else -> ProfileType.NONE
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt
new file mode 100644
index 0000000..6c35b23
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot.policy
+
+import android.app.ActivityTaskManager.RootTaskInfo
+import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT
+import android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM
+import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.content.ComponentName
+import android.graphics.Rect
+import android.os.UserHandle
+import android.view.Display
+import com.android.systemui.screenshot.data.model.ChildTaskModel
+import com.android.systemui.screenshot.policy.ActivityType.Standard
+import com.android.systemui.screenshot.policy.WindowingMode.FullScreen
+
+/** An enum mapping to [android.app.WindowConfiguration] constants via [toInt]. */
+enum class ActivityType(private val intValue: Int) {
+ Undefined(ACTIVITY_TYPE_UNDEFINED),
+ Standard(ACTIVITY_TYPE_STANDARD),
+ Home(ACTIVITY_TYPE_HOME),
+ Recents(ACTIVITY_TYPE_RECENTS),
+ Assistant(ACTIVITY_TYPE_ASSISTANT),
+ Dream(ACTIVITY_TYPE_DREAM);
+
+ /** Returns the [android.app.WindowConfiguration] int constant for the type. */
+ fun toInt() = intValue
+}
+
+/** An enum mapping to [android.app.WindowConfiguration] constants via [toInt]. */
+enum class WindowingMode(private val intValue: Int) {
+ Undefined(WINDOWING_MODE_UNDEFINED),
+ FullScreen(WINDOWING_MODE_FULLSCREEN),
+ PictureInPicture(WINDOWING_MODE_PINNED),
+ Freeform(WINDOWING_MODE_FREEFORM),
+ MultiWindow(WINDOWING_MODE_MULTI_WINDOW);
+
+ /** Returns the [android.app.WindowConfiguration] int constant for the mode. */
+ fun toInt() = intValue
+}
+
+/**
+ * Constructs a child task for a [RootTaskInfo], copying [RootTaskInfo.bounds] and
+ * [RootTaskInfo.userId] from the parent by default.
+ */
+fun RootTaskInfo.newChildTask(
+ taskId: Int,
+ name: String,
+ bounds: Rect? = null,
+ userId: Int? = null
+): ChildTaskModel {
+ return ChildTaskModel(taskId, name, bounds ?: this.bounds, userId ?: this.userId)
+}
+
+/** Constructs a new [RootTaskInfo]. */
+fun newRootTaskInfo(
+ taskId: Int,
+ userId: Int = UserHandle.USER_SYSTEM,
+ displayId: Int = Display.DEFAULT_DISPLAY,
+ visible: Boolean = true,
+ running: Boolean = true,
+ activityType: ActivityType = Standard,
+ windowingMode: WindowingMode = FullScreen,
+ bounds: Rect? = null,
+ topActivity: ComponentName? = null,
+ topActivityType: ActivityType = Standard,
+ numActivities: Int? = null,
+ childTaskListBuilder: RootTaskInfo.() -> List<ChildTaskModel>,
+): RootTaskInfo {
+ return RootTaskInfo().apply {
+ configuration.windowConfiguration.apply {
+ setWindowingMode(windowingMode.toInt())
+ setActivityType(activityType.toInt())
+ setBounds(bounds)
+ }
+ this.bounds = bounds
+ this.displayId = displayId
+ this.userId = userId
+ this.taskId = taskId
+ this.visible = visible
+ this.isVisible = visible
+ this.isRunning = running
+ this.topActivity = topActivity
+ this.topActivityType = topActivityType.toInt()
+ // NOTE: topActivityInfo is _not_ populated by this code
+
+ val childTasks = childTaskListBuilder(this)
+ this.numActivities = numActivities ?: childTasks.size
+
+ childTaskNames = childTasks.map { it.name }.toTypedArray()
+ childTaskIds = childTasks.map { it.id }.toIntArray()
+ childTaskBounds = childTasks.map { it.bounds }.toTypedArray()
+ childTaskUserIds = childTasks.map { it.userId }.toIntArray()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
new file mode 100644
index 0000000..9e3ae05
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot.policy
+
+import android.content.ComponentName
+import android.os.UserHandle
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.screenshot.data.model.DisplayContentModel
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.FILES
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.YOUTUBE
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.YOUTUBE_PIP
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks.emptyRootSplit
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks.fullScreen
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks.launcher
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.TaskSpec
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.pictureInPictureApp
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.singleFullScreen
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.splitScreenApps
+import com.android.systemui.screenshot.data.model.SystemUiState
+import com.android.systemui.screenshot.data.repository.profileTypeRepository
+import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.Matched
+import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.NotMatched
+import com.android.systemui.screenshot.policy.CaptureType.FullScreen
+import com.android.systemui.screenshot.policy.TestUserIds.PERSONAL
+import com.android.systemui.screenshot.policy.TestUserIds.PRIVATE
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+class PrivateProfilePolicyTest {
+ private val kosmos = Kosmos()
+ private val policy = PrivateProfilePolicy(kosmos.profileTypeRepository)
+
+ // TODO:
+ // private app in PIP
+ // private app below personal PIP app
+ // Freeform windows
+
+ @Test
+ fun shadeExpanded_notMatched() = runTest {
+ val result =
+ policy.check(
+ singleFullScreen(
+ spec = TaskSpec(taskId = 1002, name = YOUTUBE, userId = PRIVATE),
+ shadeExpanded = true
+ )
+ )
+
+ assertThat(result)
+ .isEqualTo(NotMatched(PrivateProfilePolicy.NAME, PrivateProfilePolicy.SHADE_EXPANDED))
+ }
+
+ @Test
+ fun noPrivate_notMatched() = runTest {
+ val result =
+ policy.check(
+ singleFullScreen(TaskSpec(taskId = 1002, name = YOUTUBE, userId = PERSONAL))
+ )
+
+ assertThat(result)
+ .isEqualTo(NotMatched(PrivateProfilePolicy.NAME, PrivateProfilePolicy.NO_VISIBLE_TASKS))
+ }
+
+ @Test
+ fun withPrivateFullScreen_isMatched() = runTest {
+ val result =
+ policy.check(
+ singleFullScreen(TaskSpec(taskId = 1002, name = YOUTUBE, userId = PRIVATE))
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ Matched(
+ PrivateProfilePolicy.NAME,
+ PrivateProfilePolicy.PRIVATE_TASK_VISIBLE,
+ CaptureParameters(
+ type = FullScreen(displayId = 0),
+ component = ComponentName.unflattenFromString(YOUTUBE),
+ owner = UserHandle.of(PRIVATE)
+ )
+ )
+ )
+ }
+
+ @Test
+ fun withPrivateNotVisible_notMatched() = runTest {
+ val result =
+ policy.check(
+ DisplayContentModel(
+ displayId = 0,
+ systemUiState = SystemUiState(shadeExpanded = false),
+ rootTasks =
+ listOf(
+ fullScreen(
+ TaskSpec(taskId = 1002, name = FILES, userId = PERSONAL),
+ visible = true
+ ),
+ fullScreen(
+ TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
+ visible = false
+ ),
+ launcher(visible = false),
+ emptyRootSplit,
+ )
+ )
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ NotMatched(
+ PrivateProfilePolicy.NAME,
+ PrivateProfilePolicy.NO_VISIBLE_TASKS,
+ )
+ )
+ }
+
+ @Test
+ fun withPrivateFocusedInSplitScreen_isMatched() = runTest {
+ val result =
+ policy.check(
+ splitScreenApps(
+ top = TaskSpec(taskId = 1002, name = FILES, userId = PERSONAL),
+ bottom = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
+ focusedTaskId = 1003
+ )
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ Matched(
+ PrivateProfilePolicy.NAME,
+ PrivateProfilePolicy.PRIVATE_TASK_VISIBLE,
+ CaptureParameters(
+ type = FullScreen(displayId = 0),
+ component = ComponentName.unflattenFromString(YOUTUBE),
+ owner = UserHandle.of(PRIVATE)
+ )
+ )
+ )
+ }
+
+ @Test
+ fun withPrivateNotFocusedInSplitScreen_isMatched() = runTest {
+ val result =
+ policy.check(
+ splitScreenApps(
+ top = TaskSpec(taskId = 1002, name = FILES, userId = PERSONAL),
+ bottom = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
+ focusedTaskId = 1002
+ )
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ Matched(
+ PrivateProfilePolicy.NAME,
+ PrivateProfilePolicy.PRIVATE_TASK_VISIBLE,
+ CaptureParameters(
+ type = FullScreen(displayId = 0),
+ component = ComponentName.unflattenFromString(FILES),
+ owner = UserHandle.of(PRIVATE)
+ )
+ )
+ )
+ }
+
+ @Test
+ fun withPrivatePictureInPictureApp_isMatched() = runTest {
+ val result =
+ policy.check(
+ pictureInPictureApp(TaskSpec(taskId = 1002, name = YOUTUBE_PIP, userId = PRIVATE))
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ Matched(
+ PrivateProfilePolicy.NAME,
+ PrivateProfilePolicy.PRIVATE_TASK_VISIBLE,
+ CaptureParameters(
+ type = FullScreen(displayId = 0),
+ component = ComponentName.unflattenFromString(YOUTUBE_PIP),
+ owner = UserHandle.of(PRIVATE)
+ )
+ )
+ )
+ }
+
+ @Test
+ fun withPrivateAppBelowPictureInPictureApp_isMatched() = runTest {
+ val result =
+ policy.check(
+ pictureInPictureApp(
+ pip = TaskSpec(taskId = 1002, name = YOUTUBE_PIP, userId = PERSONAL),
+ fullScreen = TaskSpec(taskId = 1003, name = FILES, userId = PRIVATE),
+ )
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ Matched(
+ PrivateProfilePolicy.NAME,
+ PrivateProfilePolicy.PRIVATE_TASK_VISIBLE,
+ CaptureParameters(
+ type = FullScreen(displayId = 0),
+ component = ComponentName.unflattenFromString(YOUTUBE_PIP),
+ owner = UserHandle.of(PRIVATE)
+ )
+ )
+ )
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/TestUserIds.kt
similarity index 73%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/TestUserIds.kt
index f6140f5..7a6d280 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/TestUserIds.kt
@@ -14,9 +14,13 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.screenshot.policy
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import android.os.UserHandle
+
+object TestUserIds {
+ val PERSONAL: Int = UserHandle.USER_SYSTEM
+ val WORK: Int = 10
+ val CLONE: Int = 11
+ val PRIVATE: Int = 12
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
new file mode 100644
index 0000000..5d35528
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot.policy
+
+import android.content.ComponentName
+import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.screenshot.data.model.DisplayContentModel
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.FILES
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.YOUTUBE
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FREE_FORM
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FULL_SCREEN
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.SPLIT_TOP
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.TaskSpec
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.freeFormApps
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.pictureInPictureApp
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.singleFullScreen
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.splitScreenApps
+import com.android.systemui.screenshot.data.model.SystemUiState
+import com.android.systemui.screenshot.data.repository.profileTypeRepository
+import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult
+import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.NotMatched
+import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask
+import com.android.systemui.screenshot.policy.TestUserIds.PERSONAL
+import com.android.systemui.screenshot.policy.TestUserIds.WORK
+import com.android.systemui.screenshot.policy.WorkProfilePolicy.Companion.DESKTOP_MODE_ENABLED
+import com.android.systemui.screenshot.policy.WorkProfilePolicy.Companion.SHADE_EXPANDED
+import com.android.systemui.screenshot.policy.WorkProfilePolicy.Companion.WORK_TASK_IS_TOP
+import com.android.systemui.screenshot.policy.WorkProfilePolicy.Companion.WORK_TASK_NOT_TOP
+import com.android.window.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+
+class WorkProfilePolicyTest {
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+ private val kosmos = Kosmos()
+ private val policy = WorkProfilePolicy(kosmos.profileTypeRepository)
+
+ /**
+ * There is no guarantee that every RootTaskInfo contains a non-empty list of child tasks. Test
+ * the case where the RootTaskInfo would match but child tasks are empty.
+ */
+ @Test
+ fun withEmptyChildTasks_notMatched() = runTest {
+ val result =
+ policy.check(
+ DisplayContentModel(
+ displayId = 0,
+ systemUiState = SystemUiState(shadeExpanded = false),
+ rootTasks = listOf(RootTasks.emptyWithNoChildTasks)
+ )
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ NotMatched(
+ WorkProfilePolicy.NAME,
+ WORK_TASK_NOT_TOP,
+ )
+ )
+ }
+
+ @Test
+ fun noWorkApp_notMatched() = runTest {
+ val result =
+ policy.check(
+ singleFullScreen(TaskSpec(taskId = 1002, name = YOUTUBE, userId = PERSONAL))
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ NotMatched(
+ WorkProfilePolicy.NAME,
+ WORK_TASK_NOT_TOP,
+ )
+ )
+ }
+
+ @Test
+ fun withWorkFullScreen_shadeExpanded_notMatched() = runTest {
+ val result =
+ policy.check(
+ singleFullScreen(
+ TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+ shadeExpanded = true
+ )
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ NotMatched(
+ WorkProfilePolicy.NAME,
+ SHADE_EXPANDED,
+ )
+ )
+ }
+
+ @Test
+ fun withWorkFullScreen_matched() = runTest {
+ val result =
+ policy.check(singleFullScreen(TaskSpec(taskId = 1002, name = FILES, userId = WORK)))
+
+ assertThat(result)
+ .isEqualTo(
+ PolicyResult.Matched(
+ policy = WorkProfilePolicy.NAME,
+ reason = WORK_TASK_IS_TOP,
+ CaptureParameters(
+ type = IsolatedTask(taskId = 1002, taskBounds = FULL_SCREEN),
+ component = ComponentName.unflattenFromString(FILES),
+ owner = UserHandle.of(WORK),
+ )
+ )
+ )
+ }
+
+ @Test
+ fun withWorkFocusedInSplitScreen_matched() = runTest {
+ val result =
+ policy.check(
+ splitScreenApps(
+ top = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+ bottom = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
+ focusedTaskId = 1002
+ )
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ PolicyResult.Matched(
+ policy = WorkProfilePolicy.NAME,
+ reason = WORK_TASK_IS_TOP,
+ CaptureParameters(
+ type = IsolatedTask(taskId = 1002, taskBounds = SPLIT_TOP),
+ component = ComponentName.unflattenFromString(FILES),
+ owner = UserHandle.of(WORK),
+ )
+ )
+ )
+ }
+
+ @Test
+ fun withWorkNotFocusedInSplitScreen_notMatched() = runTest {
+ val result =
+ policy.check(
+ splitScreenApps(
+ top = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+ bottom = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
+ focusedTaskId = 1003
+ )
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ NotMatched(
+ WorkProfilePolicy.NAME,
+ WORK_TASK_NOT_TOP,
+ )
+ )
+ }
+
+ @Test
+ fun withWorkBelowPersonalPictureInPicture_matched() = runTest {
+ val result =
+ policy.check(
+ pictureInPictureApp(
+ pip = TaskSpec(taskId = 1002, name = YOUTUBE, userId = PERSONAL),
+ fullScreen = TaskSpec(taskId = 1003, name = FILES, userId = WORK),
+ )
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ PolicyResult.Matched(
+ policy = WorkProfilePolicy.NAME,
+ reason = WORK_TASK_IS_TOP,
+ CaptureParameters(
+ type = IsolatedTask(taskId = 1003, taskBounds = FULL_SCREEN),
+ component = ComponentName.unflattenFromString(FILES),
+ owner = UserHandle.of(WORK),
+ )
+ )
+ )
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun withWorkFocusedInFreeForm_matched() = runTest {
+ val result =
+ policy.check(
+ freeFormApps(
+ TaskSpec(taskId = 1002, name = YOUTUBE, userId = PERSONAL),
+ TaskSpec(taskId = 1003, name = FILES, userId = WORK),
+ focusedTaskId = 1003
+ )
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ PolicyResult.Matched(
+ policy = WorkProfilePolicy.NAME,
+ reason = WORK_TASK_IS_TOP,
+ CaptureParameters(
+ type = IsolatedTask(taskId = 1003, taskBounds = FREE_FORM),
+ component = ComponentName.unflattenFromString(FILES),
+ owner = UserHandle.of(WORK),
+ )
+ )
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun withWorkFocusedInFreeForm_desktopModeEnabled_notMatched() = runTest {
+ val result =
+ policy.check(
+ freeFormApps(
+ TaskSpec(taskId = 1002, name = YOUTUBE, userId = PERSONAL),
+ TaskSpec(taskId = 1003, name = FILES, userId = WORK),
+ focusedTaskId = 1003
+ )
+ )
+
+ assertThat(result)
+ .isEqualTo(
+ NotMatched(
+ WorkProfilePolicy.NAME,
+ DESKTOP_MODE_ENABLED,
+ )
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index e611da0..ee03236 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -36,6 +36,7 @@
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -85,6 +86,7 @@
@Mock private lateinit var communalViewModel: CommunalViewModel
@Mock private lateinit var powerManager: PowerManager
@Mock private lateinit var dialogFactory: SystemUIDialogFactory
+ @Mock private lateinit var communalColors: CommunalColors
private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
private lateinit var shadeInteractor: ShadeInteractor
private lateinit var keyguardInteractor: KeyguardInteractor
@@ -116,6 +118,7 @@
keyguardInteractor,
shadeInteractor,
powerManager,
+ communalColors,
kosmos.sceneDataSourceDelegator,
)
testableLooper = TestableLooper.get(this)
@@ -156,6 +159,7 @@
keyguardInteractor,
shadeInteractor,
powerManager,
+ communalColors,
kosmos.sceneDataSourceDelegator,
)
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 dfe72cf..2a5332e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -154,6 +154,7 @@
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger;
import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository;
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -161,6 +162,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.data.repository.FakeHeadsUpNotificationRepository;
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
@@ -358,6 +360,10 @@
protected TestScope mTestScope = mKosmos.getTestScope();
protected ShadeInteractor mShadeInteractor;
protected PowerInteractor mPowerInteractor;
+ protected FakeHeadsUpNotificationRepository mFakeHeadsUpNotificationRepository =
+ new FakeHeadsUpNotificationRepository();
+ protected HeadsUpNotificationInteractor mHeadsUpNotificationInteractor =
+ new HeadsUpNotificationInteractor(mFakeHeadsUpNotificationRepository);
protected NotificationPanelViewController.TouchHandler mTouchHandler;
protected ConfigurationController mConfigurationController;
protected SysuiStatusBarStateController mStatusBarStateController;
@@ -398,7 +404,7 @@
mFakeKeyguardRepository = keyguardInteractorDeps.getRepository();
mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor(mFakeKeyguardRepository);
mFakeKeyguardClockRepository = new FakeKeyguardClockRepository();
- mKeyguardClockInteractor = new KeyguardClockInteractor(mFakeKeyguardClockRepository);
+ mKeyguardClockInteractor = mKosmos.getKeyguardClockInteractor();
mKeyguardInteractor = keyguardInteractorDeps.getKeyguardInteractor();
mShadeRepository = new FakeShadeRepository();
mShadeAnimationInteractor = new ShadeAnimationInteractorLegacyImpl(
@@ -410,6 +416,8 @@
mock(DeviceEntryUdfpsInteractor.class);
when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(MutableStateFlow(false));
+ when(mKeyguardTransitionInteractor.isInTransitionToState(any())).thenReturn(emptyFlow());
+
mShadeInteractor = new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
mKosmos.getDeviceProvisioningInteractor(),
@@ -539,7 +547,7 @@
}).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class));
// Dreaming->Lockscreen
- when(mKeyguardTransitionInteractor.getDreamingToLockscreenTransition())
+ when(mKeyguardTransitionInteractor.transition(any(), any()))
.thenReturn(emptyFlow());
when(mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha())
.thenReturn(emptyFlow());
@@ -547,46 +555,28 @@
.thenReturn(emptyFlow());
// Occluded->Lockscreen
- when(mKeyguardTransitionInteractor.getOccludedToLockscreenTransition())
- .thenReturn(emptyFlow());
when(mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha())
.thenReturn(emptyFlow());
when(mOccludedToLockscreenTransitionViewModel.getLockscreenTranslationY())
.thenReturn(emptyFlow());
// Lockscreen->Dreaming
- when(mKeyguardTransitionInteractor.getLockscreenToDreamingTransition())
- .thenReturn(emptyFlow());
when(mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha())
.thenReturn(emptyFlow());
when(mLockscreenToDreamingTransitionViewModel.lockscreenTranslationY(anyInt()))
.thenReturn(emptyFlow());
// Gone->Dreaming
- when(mKeyguardTransitionInteractor.getGoneToDreamingTransition())
- .thenReturn(emptyFlow());
when(mGoneToDreamingTransitionViewModel.getLockscreenAlpha())
.thenReturn(emptyFlow());
when(mGoneToDreamingTransitionViewModel.lockscreenTranslationY(anyInt()))
.thenReturn(emptyFlow());
// Gone->Dreaming lockscreen hosted
- when(mKeyguardTransitionInteractor.getGoneToDreamingLockscreenHostedTransition())
- .thenReturn(emptyFlow());
when(mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha())
.thenReturn(emptyFlow());
- // Dreaming lockscreen hosted->Lockscreen
- when(mKeyguardTransitionInteractor.getDreamingLockscreenHostedToLockscreenTransition())
- .thenReturn(emptyFlow());
-
- // Lockscreen->Dreaming lockscreen hosted
- when(mKeyguardTransitionInteractor.getLockscreenToDreamingLockscreenHostedTransition())
- .thenReturn(emptyFlow());
-
// Lockscreen->Occluded
- when(mKeyguardTransitionInteractor.getLockscreenToOccludedTransition())
- .thenReturn(emptyFlow());
when(mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha())
.thenReturn(emptyFlow());
when(mLockscreenToOccludedTransitionViewModel.getLockscreenTranslationY())
@@ -746,6 +736,7 @@
mActivityStarter,
mSharedNotificationContainerInteractor,
mActiveNotificationsInteractor,
+ mHeadsUpNotificationInteractor,
mShadeAnimationInteractor,
mKeyguardViewConfigurator,
mDeviceEntryFaceAuthInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 650c45b..81e20c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -46,6 +46,7 @@
import android.animation.ValueAnimator;
import android.graphics.Point;
import android.os.PowerManager;
+import android.platform.test.annotations.DisableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
@@ -62,6 +63,7 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
+import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
@@ -1287,6 +1289,7 @@
}
@Test
+ @DisableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
public void shadeExpanded_whenHunIsPresent() {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
index 4df7ef5..6631d29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
@@ -14,8 +14,11 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.shade
+import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.HapticFeedbackConstants
@@ -29,10 +32,14 @@
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.StatusBarState.SHADE
import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
+import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
+import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
+import com.android.systemui.statusbar.notification.stack.data.repository.setNotifications
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.advanceUntilIdle
@@ -235,4 +242,41 @@
val bottomAreaAlpha by collectLastValue(mFakeKeyguardRepository.bottomAreaAlpha)
assertThat(bottomAreaAlpha).isEqualTo(1f)
}
+
+ @Test
+ @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ fun shadeExpanded_whenHunIsPresent() = runTest {
+ launch(mainDispatcher) {
+ givenViewAttached()
+
+ // WHEN a pinned heads up is present
+ mFakeHeadsUpNotificationRepository.setNotifications(
+ fakeHeadsUpRowRepository("key", isPinned = true)
+ )
+ }
+ advanceUntilIdle()
+
+ // THEN the panel should be visible
+ assertThat(mNotificationPanelViewController.isExpanded).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ fun shadeExpanded_whenHunIsAnimatingAway() = runTest {
+ launch(mainDispatcher) {
+ givenViewAttached()
+
+ // WHEN a heads up is animating away
+ mFakeHeadsUpNotificationRepository.isHeadsUpAnimatingAway.value = true
+ }
+ advanceUntilIdle()
+
+ // THEN the panel should be visible
+ assertThat(mNotificationPanelViewController.isExpanded).isTrue()
+ }
+
+ private fun fakeHeadsUpRowRepository(key: String, isPinned: Boolean = false) =
+ FakeHeadsUpRowRepository(key = key, elementKey = Any()).apply {
+ this.isPinned.value = isPinned
+ }
}
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 cf7c6f4..b89ccef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -75,8 +75,6 @@
import com.android.systemui.scene.FakeWindowRootViewComponent;
import com.android.systemui.scene.data.repository.SceneContainerRepository;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.scene.shared.logger.SceneLogger;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
@@ -136,7 +134,6 @@
@Mock private ShadeWindowLogger mShadeWindowLogger;
@Mock private SelectedUserInteractor mSelectedUserInteractor;
@Mock private UserTracker mUserTracker;
- @Mock private SceneContainerFlags mSceneContainerFlags;
@Mock private LargeScreenHeaderHelper mLargeScreenHeaderHelper;
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
@@ -185,14 +182,12 @@
mKosmos.getDeviceUnlockedInteractor());
FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
- FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags();
KeyguardTransitionInteractor keyguardTransitionInteractor =
mKosmos.getKeyguardTransitionInteractor();
KeyguardInteractor keyguardInteractor = new KeyguardInteractor(
keyguardRepository,
new FakeCommandQueue(),
powerInteractor,
- sceneContainerFlags,
new FakeKeyguardBouncerRepository(),
new ConfigurationInteractor(configurationRepository),
shadeRepository,
@@ -256,7 +251,6 @@
mShadeWindowLogger,
() -> mSelectedUserInteractor,
mUserTracker,
- mSceneContainerFlags,
() -> communalInteractor) {
@Override
protected boolean isDebuggable() {
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 2c0a15d..8c5a4d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -43,6 +43,8 @@
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
@@ -160,7 +162,7 @@
.thenReturn(keyguardBouncerComponent)
whenever(keyguardBouncerComponent.securityContainerController)
.thenReturn(keyguardSecurityContainerController)
- whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
+ whenever(keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING))
.thenReturn(emptyFlow<TransitionStep>())
featureFlagsClassic = FakeFeatureFlagsClassic()
@@ -576,6 +578,14 @@
assertEquals(keyEvent, falsingCollector.lastKeyEvent)
}
+ @Test
+ fun cancelCurrentTouch_callsDragDownHelper() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+ underTest.cancelCurrentTouch()
+
+ verify(dragDownHelper).stopDragging()
+ }
+
companion object {
private val DOWN_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
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 98a815c..ba8eb6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -36,6 +36,8 @@
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
@@ -149,7 +151,7 @@
whenever(statusBarStateController.isDozing).thenReturn(false)
mDependency.injectTestDependency(ShadeController::class.java, shadeController)
whenever(dockManager.isDocked).thenReturn(false)
- whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
+ whenever(keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING))
.thenReturn(emptyFlow())
val featureFlags = FakeFeatureFlags()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 20d877e..77ad17a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -62,7 +62,6 @@
import com.android.systemui.res.R;
import com.android.systemui.scene.data.repository.SceneContainerRepository;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
import com.android.systemui.scene.shared.logger.SceneLogger;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
@@ -208,14 +207,12 @@
mock(SceneLogger.class),
mKosmos.getDeviceUnlockedInteractor());
- FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags();
KeyguardTransitionInteractor keyguardTransitionInteractor =
mKosmos.getKeyguardTransitionInteractor();
KeyguardInteractor keyguardInteractor = new KeyguardInteractor(
mKeyguardRepository,
new FakeCommandQueue(),
powerInteractor,
- sceneContainerFlags,
new FakeKeyguardBouncerRepository(),
new ConfigurationInteractor(configurationRepository),
mShadeRepository,
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 433c95a..6bdd3ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -33,7 +33,6 @@
import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
@@ -95,7 +94,6 @@
headsUpManager,
PowerInteractorFactory.create().powerInteractor,
ActiveNotificationsInteractor(activeNotificationsRepository, testDispatcher),
- kosmos.sceneContainerFlags,
kosmos::sceneInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
index f2abb90..7c33648 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
@@ -1,15 +1,14 @@
package com.android.systemui.shade.transition
-import android.platform.test.annotations.DisableFlags
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -70,7 +69,7 @@
}
@Test
- @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
fun onPanelExpansionChanged_setsFractionEqualToEventFraction() {
underTest.onPanelExpansionChanged(DEFAULT_EXPANSION_EVENT)
@@ -78,7 +77,7 @@
}
@Test
- @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
fun onPanelStateChanged_forwardsToScrimTransitionController() {
startLegacyPanelExpansion()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
index eb418fd..4679a58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
@@ -19,11 +19,12 @@
import android.content.Context
import android.content.res.Resources
import android.content.res.TypedArray
+import android.platform.test.annotations.PlatinumTest
import android.testing.AndroidTestingRunner
import android.util.AttributeSet
import androidx.test.filters.SmallTest
-import com.android.systemui.shared.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.R
import com.android.systemui.shared.shadow.DoubleShadowTextClock
import com.android.systemui.util.mockito.whenever
import junit.framework.Assert.assertTrue
@@ -36,6 +37,7 @@
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
+@PlatinumTest(focusArea = "sysui")
@SmallTest
@RunWith(AndroidTestingRunner::class)
class DoubleShadowTextClockTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
index 56fc0c7..a05a23b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
@@ -21,7 +21,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -34,21 +34,19 @@
@RunWith(AndroidTestingRunner::class)
class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() {
- private val progressProvider = TestUnfoldTransitionProvider()
+ private val progressProvider = FakeUnfoldTransitionProvider()
- @Mock
- private lateinit var parent: ViewGroup
+ @Mock private lateinit var parent: ViewGroup
- @Mock
- private lateinit var shouldBeAnimated: () -> Boolean
+ @Mock private lateinit var shouldBeAnimated: () -> Boolean
private lateinit var animator: UnfoldConstantTranslateAnimator
private val viewsIdToRegister
get() =
setOf(
- ViewIdToTranslate(START_VIEW_ID, Direction.START, shouldBeAnimated),
- ViewIdToTranslate(END_VIEW_ID, Direction.END, shouldBeAnimated)
+ ViewIdToTranslate(START_VIEW_ID, Direction.START, shouldBeAnimated),
+ ViewIdToTranslate(END_VIEW_ID, Direction.END, shouldBeAnimated)
)
@Before
@@ -122,11 +120,12 @@
progressProvider.onTransitionStarted()
progressProvider.onTransitionProgress(0f)
- val rtlMultiplier = if (layoutDirection == View.LAYOUT_DIRECTION_LTR) {
- 1
- } else {
- -1
- }
+ val rtlMultiplier =
+ if (layoutDirection == View.LAYOUT_DIRECTION_LTR) {
+ 1
+ } else {
+ -1
+ }
list.forEach { (view, direction) ->
assertEquals(
(-MAX_TRANSLATION * direction * rtlMultiplier).toInt(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index de61086..be5af88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -19,12 +19,10 @@
package com.android.systemui.statusbar
import android.animation.ObjectAnimator
-import android.platform.test.annotations.DisableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
@@ -35,6 +33,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.jank.interactionJankMonitor
@@ -56,7 +55,6 @@
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.data.repository.FakeShadeRepository
@@ -88,8 +86,8 @@
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -136,7 +134,6 @@
val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val featureFlags = FakeFeatureFlagsClassic()
val shadeRepository = FakeShadeRepository()
- val sceneContainerFlags = FakeSceneContainerFlags()
val configurationRepository = FakeConfigurationRepository()
val keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
fromLockscreenTransitionInteractor = kosmos.fromLockscreenTransitionInteractor
@@ -147,7 +144,6 @@
keyguardRepository,
FakeCommandQueue(),
powerInteractor,
- sceneContainerFlags,
FakeKeyguardBouncerRepository(),
ConfigurationInteractor(configurationRepository),
shadeRepository,
@@ -187,7 +183,7 @@
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
fun testChangeState_logged() {
TestableLooper.get(this).runWithLooper {
underTest.state = StatusBarState.KEYGUARD
@@ -214,7 +210,7 @@
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
fun testSetState_appliesState_sameStateButDifferentUpcomingState() {
underTest.state = StatusBarState.SHADE
underTest.setUpcomingState(StatusBarState.KEYGUARD)
@@ -227,7 +223,7 @@
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
fun testSetState_appliesState_differentStateEqualToUpcomingState() {
underTest.state = StatusBarState.SHADE
underTest.setUpcomingState(StatusBarState.KEYGUARD)
@@ -239,7 +235,7 @@
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
fun testSetState_doesNotApplyState_currentAndUpcomingStatesSame() {
underTest.state = StatusBarState.SHADE
underTest.setUpcomingState(StatusBarState.SHADE)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
index eb692eb..0e24ed4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
@@ -40,6 +40,9 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.RowContentBindParams;
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
+import com.android.systemui.statusbar.notification.row.RowInflaterTask;
+import com.android.systemui.statusbar.notification.row.RowInflaterTaskLogger;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -114,20 +117,25 @@
private NotificationEntry addGroup(int size) {
NotificationEntry summary = new NotificationEntryBuilder().build();
- summary.setRow(createRow());
+ summary.setRow(createRow(summary));
ArrayList<NotificationEntry> children = new ArrayList<>();
for (int i = 0; i < size; i++) {
NotificationEntry child = new NotificationEntryBuilder().build();
- child.setRow(createRow());
+ child.setRow(createRow(child));
children.add(child);
}
mGroupNotifs.put(summary, children);
return summary;
}
- private ExpandableNotificationRow createRow() {
+ private ExpandableNotificationRow createRow(NotificationEntry entry) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ inflater.setFactory2(
+ new RowInflaterTask.RowAsyncLayoutInflater(entry, new FakeSystemClock(), mock(
+ RowInflaterTaskLogger.class)));
+
ExpandableNotificationRow row = (ExpandableNotificationRow)
- LayoutInflater.from(mContext).inflate(R.layout.status_bar_notification_row, null);
+ inflater.inflate(R.layout.status_bar_notification_row, null);
row.getPrivateLayout().setContractedChild(new View(mContext));
row.getPrivateLayout().setExpandedChild(new View(mContext));
return row;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
index 4eb7daa..894e02e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -188,6 +188,9 @@
@Test
fun animationsEnabled_isTrue_whenStartingToSleepAndControlScreenOff() =
testComponent.runTest {
+ val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
+ assertThat(animationsEnabled).isTrue()
+
powerRepository.updateWakefulness(
rawState = WakefulnessState.STARTING_TO_SLEEP,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
@@ -201,8 +204,6 @@
)
)
whenever(dozeParams.shouldControlScreenOff()).thenReturn(true)
- val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
- runCurrent()
assertThat(animationsEnabled).isTrue()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index 35b8493..78b7615 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -195,6 +195,9 @@
@Test
fun animationsEnabled_isTrue_whenStartingToSleepAndControlScreenOff() =
testComponent.runTest {
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ assertThat(animationsEnabled).isTrue()
+
powerRepository.updateWakefulness(
rawState = WakefulnessState.STARTING_TO_SLEEP,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
@@ -208,7 +211,7 @@
)
)
whenever(dozeParams.shouldControlScreenOff()).thenReturn(true)
- val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+
runCurrent()
assertThat(animationsEnabled).isTrue()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index 5410864..edab9d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -50,7 +50,8 @@
systemClock,
uiEventLogger,
userTracker,
- avalancheProvider
+ avalancheProvider,
+ systemSettings
)
}
@@ -82,7 +83,7 @@
fun testAvalancheFilter_duringAvalanche_allowConversationFromAfterEvent() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+ withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
ensurePeekState()
assertShouldHeadsUp(buildEntry {
importance = NotificationManager.IMPORTANCE_HIGH
@@ -97,7 +98,7 @@
fun testAvalancheFilter_duringAvalanche_suppressConversationFromBeforeEvent() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+ withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
ensurePeekState()
assertShouldNotHeadsUp(buildEntry {
importance = NotificationManager.IMPORTANCE_DEFAULT
@@ -112,7 +113,7 @@
fun testAvalancheFilter_duringAvalanche_allowHighPriorityConversation() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+ withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
ensurePeekState()
assertShouldHeadsUp(buildEntry {
importance = NotificationManager.IMPORTANCE_HIGH
@@ -125,7 +126,7 @@
fun testAvalancheFilter_duringAvalanche_allowCall() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+ withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
ensurePeekState()
assertShouldHeadsUp(buildEntry {
importance = NotificationManager.IMPORTANCE_HIGH
@@ -138,7 +139,7 @@
fun testAvalancheFilter_duringAvalanche_allowCategoryReminder() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+ withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
ensurePeekState()
assertShouldHeadsUp(buildEntry {
importance = NotificationManager.IMPORTANCE_HIGH
@@ -151,7 +152,7 @@
fun testAvalancheFilter_duringAvalanche_allowCategoryEvent() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+ withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
ensurePeekState()
assertShouldHeadsUp(buildEntry {
importance = NotificationManager.IMPORTANCE_HIGH
@@ -164,7 +165,7 @@
fun testAvalancheFilter_duringAvalanche_allowFsi() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+ withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
assertFsiNotSuppressed()
}
}
@@ -173,7 +174,7 @@
fun testAvalancheFilter_duringAvalanche_allowColorized() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+ withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
ensurePeekState()
assertShouldHeadsUp(buildEntry {
importance = NotificationManager.IMPORTANCE_HIGH
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index 24f6708..3b979a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -77,6 +77,8 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.settings.FakeGlobalSettings
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.SystemSettings
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.utils.leaks.FakeBatteryController
import com.android.systemui.utils.leaks.FakeKeyguardStateController
@@ -126,6 +128,7 @@
protected val uiEventLogger = UiEventLoggerFake()
protected val userTracker = FakeUserTracker()
protected val avalancheProvider: AvalancheProvider = mock()
+ lateinit var systemSettings: SystemSettings
protected abstract val provider: VisualInterruptionDecisionProvider
@@ -153,6 +156,7 @@
deviceProvisionedController.currentUser = userId
userTracker.set(listOf(user), /* currentUserIndex = */ 0)
+ systemSettings = FakeSettings()
provider.start()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
index 620ad9c..60aaa64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
@@ -30,6 +30,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.EventLog
import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SystemSettings
import com.android.systemui.util.time.SystemClock
object VisualInterruptionDecisionProviderTestUtil {
@@ -51,7 +52,8 @@
systemClock: SystemClock,
uiEventLogger: UiEventLogger,
userTracker: UserTracker,
- avalancheProvider: AvalancheProvider
+ avalancheProvider: AvalancheProvider,
+ systemSettings: SystemSettings
): VisualInterruptionDecisionProvider {
return if (VisualInterruptionRefactor.isEnabled) {
VisualInterruptionDecisionProviderImpl(
@@ -70,7 +72,8 @@
systemClock,
uiEventLogger,
userTracker,
- avalancheProvider
+ avalancheProvider,
+ systemSettings
)
} else {
NotificationInterruptStateProviderWrapper(
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 69e0db9..bb68ec5 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
@@ -138,7 +138,6 @@
mHeadsUpManager,
mPowerInteractor,
mActiveNotificationsInteractor,
- mKosmos.getFakeSceneContainerFlags(),
() -> mKosmos.getSceneInteractor());
mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 06a4d08..aa79c23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -398,7 +398,7 @@
}
@Test
- public void testAboveShelfChangedListenerCalledHeadsUpGoingAway() throws Exception {
+ public void testAboveShelfChangedListenerCalledHeadsUpAnimatingAway() throws Exception {
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
row.setAboveShelfChangedListener(listener);
@@ -772,8 +772,7 @@
row.setUserExpanded(true);
row.setOnKeyguard(false);
row.setSensitive(/* sensitive= */true, /* hideSensitive= */false);
- row.setHideSensitive(/* hideSensitive= */true, /* animated= */false,
- /* delay= */0L, /* duration= */0L);
+ row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */true);
// THEN
assertThat(row.isExpanded()).isFalse();
@@ -787,8 +786,7 @@
row.setUserExpanded(true);
row.setOnKeyguard(false);
row.setSensitive(/* sensitive= */true, /* hideSensitive= */false);
- row.setHideSensitive(/* hideSensitive= */false, /* animated= */false,
- /* delay= */0L, /* duration= */0L);
+ row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */false);
// THEN
assertThat(row.isExpanded()).isTrue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index 91e4666..7332bc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -79,10 +79,11 @@
initMocks(this)
fakeParent =
spy(FrameLayout(mContext, /* attrs= */ null).also { it.visibility = View.GONE })
+ val mockEntry = createMockNotificationEntry()
row =
spy(
- ExpandableNotificationRow(mContext, /* attrs= */ null).apply {
- entry = createMockNotificationEntry()
+ ExpandableNotificationRow(mContext, /* attrs= */ null, mockEntry).apply {
+ entry = mockEntry
}
)
ViewUtils.attachView(fakeParent)
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 fe0d9d0..9e2856d 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
@@ -23,7 +23,6 @@
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
-import static com.android.systemui.concurrency.FakeExecutorKosmosKt.getFakeExecutor;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static junit.framework.Assert.assertNotNull;
@@ -97,7 +96,6 @@
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.android.systemui.wmshell.BubblesManager;
import org.junit.Before;
@@ -182,7 +180,6 @@
mHeadsUpManager,
PowerInteractorFactory.create().getPowerInteractor(),
mActiveNotificationsInteractor,
- mKosmos.getFakeSceneContainerFlags(),
() -> mKosmos.getSceneInteractor()
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 65a960b..1b85dfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -45,6 +45,7 @@
import com.android.internal.statusbar.statusBarService
import com.android.systemui.SysuiTestCase
import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.people.widget.PeopleSpaceWidgetManager
@@ -55,7 +56,6 @@
import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.shade.shadeControllerSceneImpl
@@ -93,6 +93,7 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
+@EnableSceneContainer
class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
private val testNotificationChannel =
NotificationChannel(
@@ -143,8 +144,6 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- val sceneContainerFlags = kosmos.fakeSceneContainerFlags
- sceneContainerFlags.enabled = true
allowTestableLooperAsMainThread()
helper = NotificationTestHelper(mContext, mDependency)
Mockito.`when`(accessibilityManager.isTouchExplorationEnabled).thenReturn(false)
@@ -156,9 +155,9 @@
headsUpManager,
create().powerInteractor,
activeNotificationsInteractor,
- sceneContainerFlags,
- { sceneInteractor },
- )
+ ) {
+ sceneInteractor
+ }
gutsManager =
NotificationGutsManager(
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 9a7b8ec..745d20d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.statusbar.notification.stack
+import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.LayoutInflater
@@ -14,6 +15,7 @@
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
@@ -457,8 +459,13 @@
expansionFraction: Float,
expectedAlpha: Float
) {
+ val sbnMock: StatusBarNotification = mock()
+ val mockEntry = mock<NotificationEntry>().apply {
+ whenever(this.sbn).thenReturn(sbnMock)
+ }
+ val row = ExpandableNotificationRow(mContext, null, mockEntry)
whenever(ambientState.lastVisibleBackgroundChild)
- .thenReturn(ExpandableNotificationRow(mContext, null))
+ .thenReturn(row)
whenever(ambientState.isExpansionChanging).thenReturn(true)
whenever(ambientState.expansionFraction).thenReturn(expansionFraction)
whenever(hostLayoutController.speedBumpIndex).thenReturn(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index abb9432..89ae9f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -20,7 +20,6 @@
import static android.view.WindowInsets.Type.ime;
import static com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION;
-import static com.android.systemui.Flags.FLAG_SCENE_CONTAINER;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.RUBBER_BAND_FACTOR_NORMAL;
@@ -72,6 +71,7 @@
import com.android.systemui.ExpandHelper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.DisableSceneContainer;
import com.android.systemui.flags.EnableSceneContainer;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
@@ -90,6 +90,7 @@
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -106,6 +107,7 @@
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
+import java.util.function.Consumer;
/**
* Tests for {@link NotificationStackScrollLayout}.
@@ -227,7 +229,7 @@
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address disabled test
+ @DisableSceneContainer // TODO(b/312473478): address disabled test
public void testUpdateStackHeight_qsExpansionZero() {
final float expansionFraction = 0.2f;
final float overExpansion = 50f;
@@ -726,7 +728,7 @@
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address lack of QS Header
+ @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
public void testInsideQSHeader_noOffset() {
ViewGroup qsHeader = mock(ViewGroup.class);
Rect boundsOnScreen = new Rect(0, 0, 1000, 1000);
@@ -743,7 +745,7 @@
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address lack of QS Header
+ @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
public void testInsideQSHeader_Offset() {
ViewGroup qsHeader = mock(ViewGroup.class);
Rect boundsOnScreen = new Rect(100, 100, 1000, 1000);
@@ -763,14 +765,14 @@
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address disabled test
+ @DisableSceneContainer // TODO(b/312473478): address disabled test
public void setFractionToShade_recomputesStackHeight() {
mStackScroller.setFractionToShade(1f);
verify(mNotificationStackSizeCalculator).computeHeight(any(), anyInt(), anyFloat());
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address disabled test
+ @DisableSceneContainer // TODO(b/312473478): address disabled test
public void testSetOwnScrollY_shadeNotClosing_scrollYChanges() {
// Given: shade is not closing, scrollY is 0
mAmbientState.setScrollY(0);
@@ -869,7 +871,7 @@
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address disabled test
+ @DisableSceneContainer // TODO(b/312473478): address disabled test
public void testSplitShade_hasTopOverscroll() {
mTestableResources
.addOverride(R.bool.config_use_split_notification_shade, /* value= */ true);
@@ -942,7 +944,7 @@
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address disabled test
+ @DisableSceneContainer // TODO(b/312473478): address disabled test
public void testSetMaxDisplayedNotifications_notifiesListeners() {
ExpandableView.OnHeightChangedListener listener =
mock(ExpandableView.OnHeightChangedListener.class);
@@ -957,7 +959,7 @@
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableSceneContainer
public void testDispatchTouchEvent_sceneContainerDisabled() {
MotionEvent event = MotionEvent.obtain(
SystemClock.uptimeMillis(),
@@ -1044,6 +1046,96 @@
assertFalse(mStackScroller.getIsBeingDragged());
}
+
+ @Test
+ @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ public void testGenerateHeadsUpDisappearEvent_setsHeadsUpAnimatingAway() {
+ // GIVEN NSSL is ready for HUN animations
+ Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ prepareStackScrollerForHunAnimations(headsUpAnimatingAwayListener);
+
+ // WHEN we generate a disappear event
+ mStackScroller.generateHeadsUpAnimation(row, /* isHeadsUp = */ false);
+
+ // THEN headsUpAnimatingAway is true
+ verify(headsUpAnimatingAwayListener).accept(true);
+ assertTrue(mStackScroller.mHeadsUpAnimatingAway);
+ }
+
+ @Test
+ @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ public void testGenerateHeadsUpDisappearEvent_stackExpanded_headsUpAnimatingAwayNotSet() {
+ // GIVEN NSSL would be ready for HUN animations, BUT it is expanded
+ Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ assertTrue("Should be expanded by default.", mStackScroller.isExpanded());
+ mStackScroller.setHeadsUpAnimatingAwayListener(headsUpAnimatingAwayListener);
+ mStackScroller.setAnimationsEnabled(true);
+ mStackScroller.setHeadsUpGoingAwayAnimationsAllowed(true);
+
+ // WHEN we generate a disappear event
+ mStackScroller.generateHeadsUpAnimation(row, /* isHeadsUp = */ false);
+
+ // THEN nothing happens
+ verify(headsUpAnimatingAwayListener, never()).accept(anyBoolean());
+ assertFalse(mStackScroller.mHeadsUpAnimatingAway);
+ }
+
+ @Test
+ @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ public void testGenerateHeadsUpDisappearEvent_pendingAppearEvent_headsUpAnimatingAwayNotSet() {
+ // GIVEN NSSL is ready for HUN animations
+ Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ prepareStackScrollerForHunAnimations(headsUpAnimatingAwayListener);
+ // BUT there is a pending appear event
+ mStackScroller.generateHeadsUpAnimation(row, /* isHeadsUp = */ true);
+
+ // WHEN we generate a disappear event
+ mStackScroller.generateHeadsUpAnimation(row, /* isHeadsUp = */ false);
+
+ // THEN nothing happens
+ verify(headsUpAnimatingAwayListener, never()).accept(anyBoolean());
+ assertFalse(mStackScroller.mHeadsUpAnimatingAway);
+ }
+
+ @Test
+ @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ public void testGenerateHeadsUpAppearEvent_headsUpAnimatingAwayNotSet() {
+ // GIVEN NSSL is ready for HUN animations
+ Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ prepareStackScrollerForHunAnimations(headsUpAnimatingAwayListener);
+
+ // WHEN we generate a disappear event
+ mStackScroller.generateHeadsUpAnimation(row, /* isHeadsUp = */ true);
+
+ // THEN headsUpAnimatingWay is not set
+ verify(headsUpAnimatingAwayListener, never()).accept(anyBoolean());
+ assertFalse(mStackScroller.mHeadsUpAnimatingAway);
+ }
+
+ @Test
+ @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ public void testOnChildAnimationsFinished_resetsheadsUpAnimatingAway() {
+ // GIVEN NSSL is ready for HUN animations
+ Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ prepareStackScrollerForHunAnimations(headsUpAnimatingAwayListener);
+
+ // AND there is a HUN animating away
+ mStackScroller.generateHeadsUpAnimation(row, /* isHeadsUp = */ false);
+ assertTrue("a HUN should be animating away", mStackScroller.mHeadsUpAnimatingAway);
+
+ // WHEN the child animations are finished
+ mStackScroller.onChildAnimationFinished();
+
+ // THEN headsUpAnimatingAway is false
+ verify(headsUpAnimatingAwayListener).accept(false);
+ assertFalse(mStackScroller.mHeadsUpAnimatingAway);
+ }
+
private MotionEvent captureTouchSentToSceneFramework() {
ArgumentCaptor<MotionEvent> captor = ArgumentCaptor.forClass(MotionEvent.class);
verify(mStackScrollLayoutController).sendTouchToSceneFramework(captor.capture());
@@ -1056,6 +1148,14 @@
mStackScroller.setStatusBarState(state);
}
+ private void prepareStackScrollerForHunAnimations(
+ Consumer<Boolean> headsUpAnimatingAwayListener) {
+ mStackScroller.setHeadsUpAnimatingAwayListener(headsUpAnimatingAwayListener);
+ mStackScroller.setIsExpanded(false);
+ mStackScroller.setAnimationsEnabled(true);
+ mStackScroller.setHeadsUpGoingAwayAnimationsAllowed(true);
+ }
+
private ExpandableNotificationRow createClearableRow() {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
NotificationEntry entry = mock(NotificationEntry.class);
@@ -1116,4 +1216,6 @@
assertThat(mActual.getY()).isEqualTo(expected.getY());
}
}
+
+ private abstract static class BooleanConsumer implements Consumer<Boolean> { }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
index 4bfd7e3..e2ac203 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
@@ -31,9 +31,9 @@
import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
import com.android.systemui.power.shared.model.WakefulnessState.STARTING_TO_SLEEP
import com.android.systemui.statusbar.policy.FakeConfigurationController
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
-import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractorImpl
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.util.animation.data.repository.FakeAnimationStatusRepository
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -59,7 +59,7 @@
private val animationStatus = FakeAnimationStatusRepository()
private val configurationController = FakeConfigurationController()
- private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider()
+ private val unfoldTransitionProgressProvider = FakeUnfoldTransitionProvider()
private val powerRepository = FakePowerRepository()
private val powerInteractor =
PowerInteractor(
@@ -69,11 +69,6 @@
statusBarStateController = mock()
)
- private val unfoldTransitionRepository =
- UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
- private val unfoldTransitionInteractor =
- UnfoldTransitionInteractorImpl(unfoldTransitionRepository)
-
private val configurationRepository =
ConfigurationRepositoryImpl(
configurationController,
@@ -83,6 +78,11 @@
)
private val configurationInteractor = ConfigurationInteractor(configurationRepository)
+ private val unfoldTransitionRepository =
+ UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
+ private val unfoldTransitionInteractor =
+ UnfoldTransitionInteractor(unfoldTransitionRepository, configurationInteractor)
+
private lateinit var configuration: Configuration
private lateinit var underTest: HideNotificationsInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 50f81ff..e9ec323 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -131,6 +131,8 @@
private SelectedUserInteractor mSelectedUserInteractor;
@Mock
private BiometricUnlockInteractor mBiometricUnlockInteractor;
+ @Mock
+ private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final FakeSystemClock mSystemClock = new FakeSystemClock();
private BiometricUnlockController mBiometricUnlockController;
@@ -167,7 +169,7 @@
() -> mSelectedUserInteractor,
mBiometricUnlockInteractor,
mock(JavaAdapter.class),
- mock(KeyguardTransitionInteractor.class)
+ mKeyguardTransitionInteractor
);
biometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
biometricUnlockController.addListener(mBiometricUnlockEventsListener);
@@ -374,6 +376,24 @@
}
@Test
+ public void onBiometricAuthenticated_whenFaceOnAlternateBouncer_dismissBouncer() {
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(false);
+ when(mKeyguardTransitionInteractor.getCurrentState())
+ .thenReturn(KeyguardState.ALTERNATE_BOUNCER);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
+ mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
+
+ verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_DISMISS_BOUNCER);
+ assertThat(mBiometricUnlockController.getBiometricType())
+ .isEqualTo(BiometricSourceType.FACE);
+ }
+
+ @Test
public void onBiometricAuthenticated_whenBypassOnBouncer_dismissBouncer() {
reset(mKeyguardBypassController);
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
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 2f153d8..041e61c 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
@@ -31,6 +31,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -101,6 +102,8 @@
import com.android.systemui.communal.shared.model.CommunalScenes;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.DisableSceneContainer;
+import com.android.systemui.flags.EnableSceneContainer;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.fragments.FragmentService;
@@ -120,7 +123,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
@@ -181,7 +184,9 @@
import com.android.systemui.util.concurrency.MessageRouterImpl;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.volume.VolumeComponent;
@@ -325,6 +330,7 @@
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings();
+ private final SystemSettings mSystemSettings = new FakeSettings();
private final FakeEventLog mFakeEventLog = new FakeEventLog();
private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
private final FakeExecutor mUiBgExecutor = new FakeExecutor(mFakeSystemClock);
@@ -333,8 +339,6 @@
private final DumpManager mDumpManager = new DumpManager();
private final ScreenLifecycle mScreenLifecycle = new ScreenLifecycle(mDumpManager);
- private final FakeSceneContainerFlags mSceneContainerFlags = new FakeSceneContainerFlags();
-
private final BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor =
mKosmos.getBrightnessMirrorShowingInteractor();
@@ -348,7 +352,9 @@
// Turn AOD on and toggle feature flag for jank fixes
mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
- mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ if (!SceneContainerFlag.isEnabled()) {
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ }
IThermalService thermalService = mock(IThermalService.class);
mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
@@ -375,7 +381,8 @@
mFakeSystemClock,
mock(UiEventLogger.class),
mUserTracker,
- mAvalancheProvider);
+ mAvalancheProvider,
+ mSystemSettings);
mVisualInterruptionDecisionProvider.start();
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
@@ -422,22 +429,25 @@
((Runnable) invocation.getArgument(0)).run();
return null;
}).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
-
- mShadeController = spy(new ShadeControllerImpl(
- mCommandQueue,
- mMainExecutor,
- mock(WindowRootViewVisibilityInteractor.class),
- mKeyguardStateController,
- mStatusBarStateController,
- mStatusBarKeyguardViewManager,
- mStatusBarWindowController,
- mDeviceProvisionedController,
- mNotificationShadeWindowController,
- 0,
- () -> mNotificationPanelViewController,
- () -> mAssistManager,
- () -> mNotificationGutsManager
- ));
+ if (SceneContainerFlag.isEnabled()) {
+ mShadeController = spy(mKosmos.getShadeController());
+ } else {
+ mShadeController = spy(new ShadeControllerImpl(
+ mCommandQueue,
+ mMainExecutor,
+ mock(WindowRootViewVisibilityInteractor.class),
+ mKeyguardStateController,
+ mStatusBarStateController,
+ mStatusBarKeyguardViewManager,
+ mStatusBarWindowController,
+ mDeviceProvisionedController,
+ mNotificationShadeWindowController,
+ 0,
+ () -> mNotificationPanelViewController,
+ () -> mAssistManager,
+ () -> mNotificationGutsManager
+ ));
+ }
mShadeController.setNotificationShadeWindowViewController(
mNotificationShadeWindowViewController);
mShadeController.setNotificationPresenter(mNotificationPresenter);
@@ -558,7 +568,6 @@
mUserTracker,
() -> mFingerprintManager,
mActivityStarter,
- mSceneContainerFlags,
mBrightnessMirrorShowingInteractor
);
mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver);
@@ -568,8 +577,7 @@
any(NotificationPanelViewController.class),
any(ShadeExpansionStateManager.class),
any(BiometricUnlockController.class),
- any(ViewGroup.class),
- any(KeyguardBypassController.class)))
+ any(ViewGroup.class)))
.thenReturn(mStatusBarKeyguardViewManager);
when(mKeyguardViewMediator.getViewMediatorCallback()).thenReturn(
@@ -1091,25 +1099,25 @@
}
@Test
+ @EnableSceneContainer
public void brightnesShowingChanged_flagEnabled_ScrimControllerNotified() {
- mSceneContainerFlags.setEnabled(true);
mCentralSurfaces.registerCallbacks();
mBrightnessMirrorShowingInteractor.setMirrorShowing(true);
mTestScope.getTestScheduler().runCurrent();
- verify(mScrimController).transitionTo(ScrimState.BRIGHTNESS_MIRROR);
+ verify(mScrimController, atLeastOnce()).transitionTo(ScrimState.BRIGHTNESS_MIRROR);
mBrightnessMirrorShowingInteractor.setMirrorShowing(false);
mTestScope.getTestScheduler().runCurrent();
ArgumentCaptor<ScrimState> captor = ArgumentCaptor.forClass(ScrimState.class);
// The default is to call the one with the callback argument
- verify(mScrimController).transitionTo(captor.capture(), any());
+ verify(mScrimController, atLeastOnce()).transitionTo(captor.capture(), any());
assertThat(captor.getValue()).isNotEqualTo(ScrimState.BRIGHTNESS_MIRROR);
}
@Test
+ @DisableSceneContainer
public void brightnesShowingChanged_flagDisabled_ScrimControllerNotified() {
- mSceneContainerFlags.setEnabled(false);
mCentralSurfaces.registerCallbacks();
mBrightnessMirrorShowingInteractor.setMirrorShowing(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
index 7362e34..6150253 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
@@ -24,6 +24,8 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
@@ -72,6 +74,7 @@
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var lockscreenUserManager: NotificationLockscreenUserManager
@Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock private lateinit var devicePostureController: DevicePostureController
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var packageManager: PackageManager
@@ -138,7 +141,8 @@
private fun initKeyguardBypassController() {
keyguardBypassController =
KeyguardBypassController(
- context,
+ context.resources,
+ context.packageManager,
testScope.backgroundScope,
tunerService,
statusBarStateController,
@@ -146,7 +150,8 @@
keyguardStateController,
shadeRepository,
devicePostureController,
- dumpManager
+ keyguardTransitionInteractor,
+ dumpManager,
)
}
@@ -302,4 +307,26 @@
job.cancel()
}
}
+
+ @Test
+ fun canBypass_bypassDisabled() {
+ context.orCreateTestableResources.addOverride(
+ R.integer.config_face_unlock_bypass_override,
+ 2 /* FACE_UNLOCK_BYPASS_NEVER */
+ )
+ initKeyguardBypassController()
+ assertThat(keyguardBypassController.canBypass()).isFalse()
+ }
+
+ @Test
+ fun canBypass_bypassEnabled_alternateBouncerShowing() {
+ context.orCreateTestableResources.addOverride(
+ R.integer.config_face_unlock_bypass_override,
+ 1 /* FACE_UNLOCK_BYPASS_ALWAYS */
+ )
+ initKeyguardBypassController()
+ whenever(keyguardTransitionInteractor.getCurrentState())
+ .thenReturn(KeyguardState.ALTERNATE_BOUNCER)
+ assertThat(keyguardBypassController.canBypass()).isTrue()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 05fd63e..a6a4f24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -20,9 +20,9 @@
import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
+import static com.android.systemui.Flags.FLAG_UPDATE_USER_SWITCHER_BACKGROUND;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.Flags.FLAG_UPDATE_USER_SWITCHER_BACKGROUND;
import static com.google.common.truth.Truth.assertThat;
@@ -172,7 +172,6 @@
mKeyguardRepository,
mCommandQueue,
PowerInteractorFactory.create().getPowerInteractor(),
- mKosmos.getFakeSceneContainerFlags(),
new FakeKeyguardBouncerRepository(),
new ConfigurationInteractor(new FakeConfigurationRepository()),
new FakeShadeRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 1463680..d365663 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -33,7 +33,6 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.res.R
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeControllerImpl
import com.android.systemui.shade.ShadeLogger
@@ -51,6 +50,8 @@
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import javax.inject.Provider
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentCaptor
@@ -61,8 +62,6 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.Optional
-import javax.inject.Provider
@SmallTest
class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@@ -296,7 +295,6 @@
Optional.of(sysuiUnfoldComponent),
Optional.of(progressProvider),
featureFlags,
- FakeSceneContainerFlags(),
userChipViewModel,
centralSurfacesImpl,
statusBarWindowStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 34605fe..f04a5ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -76,8 +76,6 @@
import com.android.systemui.bouncer.ui.BouncerViewDelegate;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -145,7 +143,6 @@
@Mock private SysUIUnfoldComponent mSysUiUnfoldComponent;
@Mock private DreamOverlayStateController mDreamOverlayStateController;
@Mock private LatencyTracker mLatencyTracker;
- private FakeFeatureFlags mFeatureFlags;
@Mock private KeyguardSecurityModel mKeyguardSecurityModel;
@Mock private PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
@Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@@ -188,11 +185,10 @@
.thenReturn(mKeyguardMessageAreaController);
when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate);
when(mBouncerViewDelegate.getBackCallback()).thenReturn(mBouncerViewDelegateBackCallback);
- mFeatureFlags = new FakeFeatureFlags();
- mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false);
mSetFlagsRule.disableFlags(
com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR,
- com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
+ com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
+ com.android.systemui.Flags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT
);
when(mNotificationShadeWindowController.getWindowRootView())
@@ -218,7 +214,6 @@
() -> mShadeController,
mLatencyTracker,
mKeyguardSecurityModel,
- mFeatureFlags,
mPrimaryBouncerCallbackInteractor,
mPrimaryBouncerInteractor,
mBouncerView,
@@ -246,8 +241,7 @@
mShadeLockscreenInteractor,
new ShadeExpansionStateManager(),
mBiometricUnlockController,
- mNotificationContainer,
- mBypassController);
+ mNotificationContainer);
mStatusBarKeyguardViewManager.show(null);
ArgumentCaptor<PrimaryBouncerExpansionCallback> callbackArgumentCaptor =
ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
@@ -728,7 +722,6 @@
() -> mShadeController,
mLatencyTracker,
mKeyguardSecurityModel,
- mFeatureFlags,
mPrimaryBouncerCallbackInteractor,
mPrimaryBouncerInteractor,
mBouncerView,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
index feff046..1ec1765 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
@@ -8,7 +8,7 @@
import android.view.WindowManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.util.mockito.whenever
@@ -23,17 +23,14 @@
@TestableLooper.RunWithLooper
class StatusBarMoveFromCenterAnimationControllerTest : SysuiTestCase() {
- @Mock
- private lateinit var windowManager: WindowManager
+ @Mock private lateinit var windowManager: WindowManager
- @Mock
- private lateinit var display: Display
+ @Mock private lateinit var display: Display
- @Mock
- private lateinit var currentActivityTypeProvider: CurrentActivityTypeProvider
+ @Mock private lateinit var currentActivityTypeProvider: CurrentActivityTypeProvider
private val view: View = View(context)
- private val progressProvider = TestUnfoldTransitionProvider()
+ private val progressProvider = FakeUnfoldTransitionProvider()
private val scopedProvider = ScopedUnfoldTransitionProgressProvider(progressProvider)
private lateinit var controller: StatusBarMoveFromCenterAnimationController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
index 1e628bd..dedd0af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
@@ -13,68 +13,103 @@
*/
package com.android.systemui.statusbar.phone
+import android.app.Dialog
import android.content.res.Configuration
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import android.view.WindowManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.mock
-import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.Mockito.verify
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
class SystemUIBottomSheetDialogTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
private val configurationController = mock<ConfigurationController>()
private val config = mock<Configuration>()
+ private val delegate = mock<DialogDelegate<Dialog>>()
private lateinit var dialog: SystemUIBottomSheetDialog
@Before
fun setup() {
- dialog = SystemUIBottomSheetDialog(mContext, configurationController)
+ dialog =
+ with(kosmos) {
+ SystemUIBottomSheetDialog(
+ context,
+ testScope.backgroundScope,
+ configurationController,
+ delegate,
+ TestLayout(),
+ 0,
+ )
+ }
}
@Test
fun onStart_registersConfigCallback() {
- dialog.show()
+ kosmos.testScope.runTest {
+ dialog.show()
+ runCurrent()
- verify(configurationController).addCallback(any())
+ verify(configurationController).addCallback(any())
+ }
}
@Test
fun onStop_unregisterConfigCallback() {
- dialog.show()
- dialog.dismiss()
+ kosmos.testScope.runTest {
+ dialog.show()
+ runCurrent()
+ dialog.dismiss()
+ runCurrent()
- verify(configurationController).removeCallback(any())
+ verify(configurationController).removeCallback(any())
+ }
}
@Test
- fun onConfigurationChanged_calledInSubclass() {
- var onConfigChangedCalled = false
- val subclass =
- object : SystemUIBottomSheetDialog(mContext, configurationController) {
- override fun onConfigurationChanged() {
- onConfigChangedCalled = true
- }
- }
+ fun onConfigurationChanged_calledInDelegate() {
+ kosmos.testScope.runTest {
+ dialog.show()
+ runCurrent()
+ val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
+ verify(configurationController).addCallback(capture(captor))
- subclass.show()
+ captor.value.onConfigChanged(config)
+ runCurrent()
- val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
- verify(configurationController).addCallback(capture(captor))
- captor.value.onConfigChanged(config)
+ verify(delegate).onConfigurationChanged(any(), any())
+ }
+ }
- assertThat(onConfigChangedCalled).isTrue()
+ private class TestLayout : SystemUIBottomSheetDialog.WindowLayout {
+ override fun calculate(): Flow<SystemUIBottomSheetDialog.WindowLayout.Layout> {
+ return flowOf(
+ SystemUIBottomSheetDialog.WindowLayout.Layout(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.MATCH_PARENT,
+ )
+ )
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 9b6940e..598b12c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -137,6 +137,7 @@
wifiRepository,
mock(),
mock(),
+ mock(),
)
demoRepo =
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 c13e830..3c13906 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
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
import android.net.ConnectivityManager
+import android.os.PersistableBundle
import android.telephony.ServiceState
import android.telephony.SignalStrength
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
@@ -99,6 +100,9 @@
)
)
+ // Use a real config, with no overrides
+ private val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_ID, PersistableBundle())
+
private lateinit var mobileRepo: FakeMobileConnectionRepository
private lateinit var carrierMergedRepo: FakeMobileConnectionRepository
@@ -680,10 +684,6 @@
telephonyManager: TelephonyManager,
): MobileConnectionRepositoryImpl {
whenever(telephonyManager.subscriptionId).thenReturn(SUB_ID)
- val systemUiCarrierConfigMock: SystemUiCarrierConfig = mock()
- whenever(systemUiCarrierConfigMock.satelliteConnectionHysteresisSeconds)
- .thenReturn(MutableStateFlow(0))
-
val realRepo =
MobileConnectionRepositoryImpl(
SUB_ID,
@@ -693,7 +693,7 @@
SEP,
connectivityManager,
telephonyManager,
- systemUiCarrierConfig = systemUiCarrierConfigMock,
+ systemUiCarrierConfig = systemUiCarrierConfig,
fakeBroadcastDispatcher,
mobileMappingsProxy = mock(),
testDispatcher,
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 f761bcf..9d14116 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
@@ -1030,6 +1030,26 @@
}
@Test
+ fun inflateSignalStrength_usesCarrierConfig() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.inflateSignalStrength)
+
+ assertThat(latest).isEqualTo(false)
+
+ systemUiCarrierConfig.processNewCarrierConfig(
+ configWithOverride(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, true)
+ )
+
+ assertThat(latest).isEqualTo(true)
+
+ systemUiCarrierConfig.processNewCarrierConfig(
+ configWithOverride(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
+ )
+
+ assertThat(latest).isEqualTo(false)
+ }
+
+ @Test
fun isAllowedDuringAirplaneMode_alwaysFalse() =
testScope.runTest {
val latest by collectLastValue(underTest.isAllowedDuringAirplaneMode)
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 07abd27..b7a3b30 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
@@ -80,6 +80,7 @@
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertTrue
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
@@ -229,6 +230,7 @@
wifiRepository,
fullConnectionFactory,
updateMonitor,
+ mock(),
)
testScope.runCurrent()
@@ -529,6 +531,7 @@
}
@Test
+ @Ignore("b/333912012")
fun testConnectionCache_clearsInvalidSubscriptions() =
testScope.runTest {
collectLastValue(underTest.subscriptions)
@@ -553,6 +556,7 @@
}
@Test
+ @Ignore("b/333912012")
fun testConnectionCache_clearsInvalidSubscriptions_includingCarrierMerged() =
testScope.runTest {
collectLastValue(underTest.subscriptions)
@@ -581,6 +585,7 @@
/** Regression test for b/261706421 */
@Test
+ @Ignore("b/333912012")
fun testConnectionsCache_clearMultipleSubscriptionsAtOnce_doesNotThrow() =
testScope.runTest {
collectLastValue(underTest.subscriptions)
@@ -604,6 +609,54 @@
}
@Test
+ fun testConnectionsCache_keepsReposCached() =
+ testScope.runTest {
+ // Collect subscriptions to start the job
+ collectLastValue(underTest.subscriptions)
+
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val repo1_1 = underTest.getRepoForSubId(SUB_1_ID)
+
+ // All subscriptions disappear
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Sub1 comes back
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val repo1_2 = underTest.getRepoForSubId(SUB_1_ID)
+
+ assertThat(repo1_1).isSameInstanceAs(repo1_2)
+ }
+
+ @Test
+ fun testConnectionsCache_doesNotDropReferencesThatHaveBeenRealized() =
+ testScope.runTest {
+ // Collect subscriptions to start the job
+ collectLastValue(underTest.subscriptions)
+
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Client grabs a reference to a repository, but doesn't keep it around
+ underTest.getRepoForSubId(SUB_1_ID)
+
+ // All subscriptions disappear
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val repo1 = underTest.getRepoForSubId(SUB_1_ID)
+
+ assertThat(repo1).isNotNull()
+ }
+
+ @Test
fun testConnectionRepository_invalidSubId_doesNotThrow() =
testScope.runTest {
underTest.getRepoForSubId(SUB_1_ID)
@@ -1063,7 +1116,8 @@
airplaneModeRepository,
wifiRepository,
fullConnectionFactory,
- updateMonitor
+ updateMonitor,
+ mock(),
)
val latest by collectLastValue(underTest.defaultDataSubRatConfig)
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 c49fcf8..dfe8023 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
@@ -43,15 +43,12 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlin.time.Duration.Companion.seconds
-import kotlin.time.DurationUnit
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -181,6 +178,22 @@
}
@Test
+ fun inflateSignalStrength_arbitrarilyAddsOneToTheReportedLevel() =
+ testScope.runTest {
+ connectionRepository.inflateSignalStrength.value = false
+ val latest by collectLastValue(underTest.signalLevelIcon)
+
+ connectionRepository.primaryLevel.value = 4
+ assertThat(latest!!.level).isEqualTo(4)
+
+ connectionRepository.inflateSignalStrength.value = true
+ connectionRepository.primaryLevel.value = 4
+
+ // when INFLATE_SIGNAL_STRENGTH is true, we add 1 to the reported signal level
+ assertThat(latest!!.level).isEqualTo(5)
+ }
+
+ @Test
fun iconGroup_three_g() =
testScope.runTest {
connectionRepository.resolvedNetworkType.value =
@@ -678,32 +691,6 @@
assertThat(latest).isInstanceOf(SignalIconModel.Satellite::class.java)
}
- @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
- @Test
- fun satBasedIcon_hasHysteresisWhenDisabled() =
- testScope.runTest {
- val latest by collectLastValue(underTest.signalLevelIcon)
-
- val hysteresisDuration = 5.seconds
- connectionRepository.satelliteConnectionHysteresisSeconds.value =
- hysteresisDuration.toInt(DurationUnit.SECONDS)
-
- connectionRepository.isNonTerrestrial.value = true
-
- assertThat(latest).isInstanceOf(SignalIconModel.Satellite::class.java)
-
- // Disable satellite
- connectionRepository.isNonTerrestrial.value = false
-
- // Satellite icon should still be visible
- assertThat(latest).isInstanceOf(SignalIconModel.Satellite::class.java)
-
- // Wait for the icon to change
- advanceTimeBy(hysteresisDuration)
-
- assertThat(latest).isInstanceOf(SignalIconModel.Cellular::class.java)
- }
-
private fun createInteractor(
overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
) =
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 83d0fe8..cec4155 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
@@ -54,6 +54,7 @@
import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.launchIn
@@ -279,6 +280,76 @@
}
@Test
+ fun contentDescription_nonInflated_invalidLevelIsNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.contentDescription)
+
+ repository.inflateSignalStrength.value = false
+ repository.setAllLevels(-1)
+ assertThat(latest).isNull()
+
+ repository.setAllLevels(100)
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun contentDescription_inflated_invalidLevelIsNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.contentDescription)
+
+ repository.inflateSignalStrength.value = true
+ repository.numberOfLevels.value = 6
+ repository.setAllLevels(-2)
+ assertThat(latest).isNull()
+
+ repository.setAllLevels(100)
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun contentDescription_nonInflated_testABunchOfLevelsForNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.contentDescription)
+
+ repository.inflateSignalStrength.value = false
+ repository.numberOfLevels.value = 5
+
+ // -1 and 5 are out of the bounds for non-inflated content descriptions
+ for (i in -1..5) {
+ repository.setAllLevels(i)
+ when (i) {
+ -1,
+ 5 -> assertWithMessage("Level $i is expected to be null").that(latest).isNull()
+ else ->
+ assertWithMessage("Level $i is expected not to be null")
+ .that(latest)
+ .isNotNull()
+ }
+ }
+ }
+
+ @Test
+ fun contentDescription_inflated_testABunchOfLevelsForNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.contentDescription)
+ repository.inflateSignalStrength.value = true
+ repository.numberOfLevels.value = 6
+ // -1 and 6 are out of the bounds for inflated content descriptions
+ // Note that the interactor adds 1 to the reported level, hence the -2 to 5 range
+ for (i in -2..5) {
+ repository.setAllLevels(i)
+ when (i) {
+ -2,
+ 5 -> assertWithMessage("Level $i is expected to be null").that(latest).isNull()
+ else ->
+ assertWithMessage("Level $i is not expected to be null")
+ .that(latest)
+ .isNotNull()
+ }
+ }
+ }
+
+ @Test
fun networkType_dataEnabled_groupIsRepresented() =
testScope.runTest {
val expected =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index 42bbe3e..c4ab943 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -26,6 +26,10 @@
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.satellite.data.prod.FakeDeviceBasedSatelliteRepository
import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
+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.WifiInteractorImpl
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import com.android.systemui.util.mockito.mock
@@ -53,6 +57,10 @@
private val deviceProvisionedRepository = FakeDeviceProvisioningRepository()
private val deviceProvisioningInteractor =
DeviceProvisioningInteractor(deviceProvisionedRepository)
+ private val connectivityRepository = FakeConnectivityRepository()
+ private val wifiRepository = FakeWifiRepository()
+ private val wifiInteractor =
+ WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
@Before
fun setUp() {
@@ -61,6 +69,7 @@
repo,
iconsInteractor,
deviceProvisioningInteractor,
+ wifiInteractor,
testScope.backgroundScope,
)
}
@@ -103,6 +112,7 @@
repo,
iconsInteractor,
deviceProvisioningInteractor,
+ wifiInteractor,
testScope.backgroundScope,
)
@@ -150,6 +160,7 @@
repo,
iconsInteractor,
deviceProvisioningInteractor,
+ wifiInteractor,
testScope.backgroundScope,
)
@@ -205,6 +216,7 @@
repo,
iconsInteractor,
deviceProvisioningInteractor,
+ wifiInteractor,
testScope.backgroundScope,
)
@@ -337,6 +349,7 @@
repo,
iconsInteractor,
deviceProvisioningInteractor,
+ wifiInteractor,
testScope.backgroundScope,
)
@@ -353,4 +366,28 @@
// THEN the value is still false, because the flag is off
assertThat(latest).isFalse()
}
+
+ @Test
+ fun isWifiActive_falseWhenWifiNotActive() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isWifiActive)
+
+ // WHEN wifi is not active
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Invalid("test"))
+
+ // THEN the interactor returns false due to the wifi network not being active
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun isWifiActive_trueWhenWifiIsActive() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isWifiActive)
+
+ // WHEN wifi is active
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
+
+ // THEN the interactor returns true due to the wifi network being active
+ assertThat(latest).isTrue()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index 1d6cd37..64f19b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -26,6 +26,10 @@
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.satellite.data.prod.FakeDeviceBasedSatelliteRepository
import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.DeviceBasedSatelliteInteractor
+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.WifiInteractorImpl
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import com.android.systemui.util.mockito.mock
@@ -44,14 +48,18 @@
private lateinit var underTest: DeviceBasedSatelliteViewModel
private lateinit var interactor: DeviceBasedSatelliteInteractor
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
-
private val repo = FakeDeviceBasedSatelliteRepository()
+ private val testScope = TestScope()
+
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
+
private val deviceProvisionedRepository = FakeDeviceProvisioningRepository()
private val deviceProvisioningInteractor =
DeviceProvisioningInteractor(deviceProvisionedRepository)
-
- private val testScope = TestScope()
+ private val connectivityRepository = FakeConnectivityRepository()
+ private val wifiRepository = FakeWifiRepository()
+ private val wifiInteractor =
+ WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
@Before
fun setUp() {
@@ -63,6 +71,7 @@
repo,
mobileIconsInteractor,
deviceProvisioningInteractor,
+ wifiInteractor,
testScope.backgroundScope,
)
@@ -253,4 +262,40 @@
// THEN icon is null because the device is not provisioned
assertThat(latest).isInstanceOf(Icon::class.java)
}
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun icon_wifiIsActive() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ // GIVEN satellite is allowed
+ repo.isSatelliteAllowedForCurrentLocation.value = true
+
+ // GIVEN all icons are OOS
+ val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+ i1.isInService.value = false
+ i1.isEmergencyOnly.value = false
+
+ // GIVEN apm is disabled
+ airplaneModeRepository.setIsAirplaneMode(false)
+
+ // GIVEN device is provisioned
+ deviceProvisionedRepository.setDeviceProvisioned(true)
+
+ // GIVEN wifi network is active
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
+
+ // THEN icon is null because the device is connected to wifi
+ assertThat(latest).isNull()
+
+ // GIVEN device loses wifi connection
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Invalid("test"))
+
+ // Wait for delay to be completed
+ advanceTimeBy(10.seconds)
+
+ // THEN icon is set because the device lost wifi connection
+ assertThat(latest).isInstanceOf(Icon::class.java)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
index ae34256..bdd3d18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.data.repository.FakeKeyguardStatusBarRepository
@@ -60,7 +59,6 @@
keyguardRepository,
mock<CommandQueue>(),
PowerInteractorFactory.create().powerInteractor,
- kosmos.fakeSceneContainerFlags,
FakeKeyguardBouncerRepository(),
ConfigurationInteractor(FakeConfigurationRepository()),
FakeShadeRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
index 7a83cfe..6f58941 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
@@ -23,14 +23,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.model.SysUiStateTest
-import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState
-import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState.EASE_IN
-import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState.EASE_OUT
-import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState.MAIN
-import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState.NOT_PLAYING
-import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationStateChangedCallback
-import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.PaintDrawCallback
-import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.RenderEffectDrawCallback
+import com.android.systemui.surfaceeffects.PaintDrawCallback
+import com.android.systemui.surfaceeffects.RenderEffectDrawCallback
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader
import com.google.common.truth.Truth.assertThat
@@ -50,8 +44,8 @@
var paintFromCallback: Paint? = null
val drawCallback =
object : PaintDrawCallback {
- override fun onDraw(loadingPaint: Paint) {
- paintFromCallback = loadingPaint
+ override fun onDraw(paint: Paint) {
+ paintFromCallback = paint
}
}
val loadingEffect =
@@ -75,8 +69,8 @@
var renderEffectFromCallback: RenderEffect? = null
val drawCallback =
object : RenderEffectDrawCallback {
- override fun onDraw(loadingRenderEffect: RenderEffect) {
- renderEffectFromCallback = loadingRenderEffect
+ override fun onDraw(renderEffect: RenderEffect) {
+ renderEffectFromCallback = renderEffect
}
}
val loadingEffect =
@@ -98,16 +92,19 @@
@Test
fun play_animationStateChangesInOrder() {
val config = TurbulenceNoiseAnimationConfig()
- val states = mutableListOf(NOT_PLAYING)
+ val states = mutableListOf(LoadingEffect.AnimationState.NOT_PLAYING)
val stateChangedCallback =
- object : AnimationStateChangedCallback {
- override fun onStateChanged(oldState: AnimationState, newState: AnimationState) {
+ object : LoadingEffect.AnimationStateChangedCallback {
+ override fun onStateChanged(
+ oldState: LoadingEffect.AnimationState,
+ newState: LoadingEffect.AnimationState
+ ) {
states.add(newState)
}
}
val drawCallback =
object : PaintDrawCallback {
- override fun onDraw(loadingPaint: Paint) {}
+ override fun onDraw(paint: Paint) {}
}
val loadingEffect =
LoadingEffect(
@@ -125,7 +122,14 @@
animatorTestRule.advanceTimeBy(config.easeOutDuration.toLong())
animatorTestRule.advanceTimeBy(500)
- assertThat(states).containsExactly(NOT_PLAYING, EASE_IN, MAIN, EASE_OUT, NOT_PLAYING)
+ assertThat(states)
+ .containsExactly(
+ LoadingEffect.AnimationState.NOT_PLAYING,
+ LoadingEffect.AnimationState.EASE_IN,
+ LoadingEffect.AnimationState.MAIN,
+ LoadingEffect.AnimationState.EASE_OUT,
+ LoadingEffect.AnimationState.NOT_PLAYING
+ )
}
@Test
@@ -133,16 +137,22 @@
val config = TurbulenceNoiseAnimationConfig()
var numPlay = 0
val stateChangedCallback =
- object : AnimationStateChangedCallback {
- override fun onStateChanged(oldState: AnimationState, newState: AnimationState) {
- if (oldState == NOT_PLAYING && newState == EASE_IN) {
+ object : LoadingEffect.AnimationStateChangedCallback {
+ override fun onStateChanged(
+ oldState: LoadingEffect.AnimationState,
+ newState: LoadingEffect.AnimationState
+ ) {
+ if (
+ oldState == LoadingEffect.AnimationState.NOT_PLAYING &&
+ newState == LoadingEffect.AnimationState.EASE_IN
+ ) {
numPlay++
}
}
}
val drawCallback =
object : PaintDrawCallback {
- override fun onDraw(loadingPaint: Paint) {}
+ override fun onDraw(paint: Paint) {}
}
val loadingEffect =
LoadingEffect(
@@ -172,9 +182,15 @@
}
var isFinished = false
val stateChangedCallback =
- object : AnimationStateChangedCallback {
- override fun onStateChanged(oldState: AnimationState, newState: AnimationState) {
- if (oldState == EASE_OUT && newState == NOT_PLAYING) {
+ object : LoadingEffect.AnimationStateChangedCallback {
+ override fun onStateChanged(
+ oldState: LoadingEffect.AnimationState,
+ newState: LoadingEffect.AnimationState
+ ) {
+ if (
+ oldState == LoadingEffect.AnimationState.EASE_OUT &&
+ newState == LoadingEffect.AnimationState.NOT_PLAYING
+ ) {
isFinished = true
}
}
@@ -205,13 +221,19 @@
val config = TurbulenceNoiseAnimationConfig(maxDuration = 1000f)
val drawCallback =
object : PaintDrawCallback {
- override fun onDraw(loadingPaint: Paint) {}
+ override fun onDraw(paint: Paint) {}
}
var isFinished = false
val stateChangedCallback =
- object : AnimationStateChangedCallback {
- override fun onStateChanged(oldState: AnimationState, newState: AnimationState) {
- if (oldState == MAIN && newState == NOT_PLAYING) {
+ object : LoadingEffect.AnimationStateChangedCallback {
+ override fun onStateChanged(
+ oldState: LoadingEffect.AnimationState,
+ newState: LoadingEffect.AnimationState
+ ) {
+ if (
+ oldState == LoadingEffect.AnimationState.MAIN &&
+ newState == LoadingEffect.AnimationState.NOT_PLAYING
+ ) {
isFinished = true
}
}
@@ -242,7 +264,7 @@
)
val drawCallback =
object : PaintDrawCallback {
- override fun onDraw(loadingPaint: Paint) {}
+ override fun onDraw(paint: Paint) {}
}
val loadingEffect =
LoadingEffect(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index 28adbce..2cdc8d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -22,6 +22,8 @@
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -31,11 +33,12 @@
import com.android.systemui.power.shared.model.WakefulnessModel
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.shared.system.SysUiStatsLog
+import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_CLOSED
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_HALF_OPEN
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
-import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractorImpl
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
@@ -86,11 +89,20 @@
private val areAnimationEnabled = MutableStateFlow(true)
private val lastWakefulnessEvent = MutableStateFlow(WakefulnessModel())
private val systemClock = FakeSystemClock()
- private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider()
+ private val configurationController = FakeConfigurationController()
+ private val configurationRepository =
+ ConfigurationRepositoryImpl(
+ configurationController,
+ context,
+ testScope.backgroundScope,
+ mock()
+ )
+ private val configurationInteractor = ConfigurationInteractor(configurationRepository)
+ private val unfoldTransitionProgressProvider = FakeUnfoldTransitionProvider()
private val unfoldTransitionRepository =
UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
private val unfoldTransitionInteractor =
- UnfoldTransitionInteractorImpl(unfoldTransitionRepository)
+ UnfoldTransitionInteractor(unfoldTransitionRepository, configurationInteractor)
@Before
fun setup() {
@@ -155,7 +167,10 @@
fun unfold_progressUnavailable_logsLatencyTillScreenTurnedOn() {
testScope.runTest {
val unfoldTransitionInteractorWithEmptyProgressProvider =
- UnfoldTransitionInteractorImpl(UnfoldTransitionRepositoryImpl(Optional.empty()))
+ UnfoldTransitionInteractor(
+ UnfoldTransitionRepositoryImpl(Optional.empty()),
+ configurationInteractor,
+ )
displaySwitchLatencyTracker =
DisplaySwitchLatencyTracker(
mockContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
index b9c7e61..fd513c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
@@ -35,7 +35,7 @@
@SmallTest
class UnfoldHapticsPlayerTest : SysuiTestCase() {
- private val progressProvider = TestUnfoldTransitionProvider()
+ private val progressProvider = FakeUnfoldTransitionProvider()
private val vibrator: Vibrator = mock()
private val testFoldProvider = TestFoldProvider()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index ba72716..2955384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.unfold.util.FoldableDeviceStates
import com.android.systemui.unfold.util.FoldableTestUtils
import com.android.systemui.util.mockito.any
+import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,45 +38,41 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
-import java.util.Optional
@RunWith(AndroidTestingRunner::class)
@SmallTest
class UnfoldLatencyTrackerTest : SysuiTestCase() {
- @Mock
- lateinit var latencyTracker: LatencyTracker
+ @Mock lateinit var latencyTracker: LatencyTracker
- @Mock
- lateinit var deviceStateManager: DeviceStateManager
+ @Mock lateinit var deviceStateManager: DeviceStateManager
- @Mock
- lateinit var screenLifecycle: ScreenLifecycle
+ @Mock lateinit var screenLifecycle: ScreenLifecycle
- @Captor
- private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
+ @Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
- @Captor
- private lateinit var screenLifecycleCaptor: ArgumentCaptor<ScreenLifecycle.Observer>
+ @Captor private lateinit var screenLifecycleCaptor: ArgumentCaptor<ScreenLifecycle.Observer>
private lateinit var deviceStates: FoldableDeviceStates
private lateinit var unfoldLatencyTracker: UnfoldLatencyTracker
- private val transitionProgressProvider = TestUnfoldTransitionProvider()
+ private val transitionProgressProvider = FakeUnfoldTransitionProvider()
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- unfoldLatencyTracker = UnfoldLatencyTracker(
- latencyTracker,
- deviceStateManager,
- Optional.of(transitionProgressProvider),
- context.mainExecutor,
- context,
- context.contentResolver,
- screenLifecycle
- ).apply { init() }
+ unfoldLatencyTracker =
+ UnfoldLatencyTracker(
+ latencyTracker,
+ deviceStateManager,
+ Optional.of(transitionProgressProvider),
+ context.mainExecutor,
+ context,
+ context.contentResolver,
+ screenLifecycle
+ )
+ .apply { init() }
deviceStates = FoldableTestUtils.findDeviceStates(context)
verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
@@ -107,7 +104,7 @@
}
@Test
- fun unfold_firstFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventNotPropagated() {
+ fun firstFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventNotPropagated() {
setAnimationsEnabled(true)
sendFoldEvent(folded = false)
@@ -118,7 +115,7 @@
}
@Test
- fun unfold_secondFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() {
+ fun secondFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() {
setAnimationsEnabled(true)
sendFoldEvent(folded = true)
sendFoldEvent(folded = false)
@@ -131,7 +128,7 @@
}
@Test
- fun unfold_unfoldFoldUnfoldAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() {
+ fun unfoldFoldUnfoldAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() {
setAnimationsEnabled(true)
sendFoldEvent(folded = false)
sendFoldEvent(folded = true)
@@ -196,4 +193,4 @@
durationScale.toString()
)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
index 6ec0251..0c452eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
@@ -17,21 +17,18 @@
@SmallTest
class UnfoldTransitionWallpaperControllerTest : SysuiTestCase() {
- @Mock
- private lateinit var wallpaperController: WallpaperController
+ @Mock private lateinit var wallpaperController: WallpaperController
- private val progressProvider = TestUnfoldTransitionProvider()
+ private val progressProvider = FakeUnfoldTransitionProvider()
- @JvmField
- @Rule
- val mockitoRule = MockitoJUnit.rule()
+ @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
private lateinit var unfoldWallpaperController: UnfoldTransitionWallpaperController
@Before
fun setup() {
- unfoldWallpaperController = UnfoldTransitionWallpaperController(progressProvider,
- wallpaperController)
+ unfoldWallpaperController =
+ UnfoldTransitionWallpaperController(progressProvider, wallpaperController)
unfoldWallpaperController.init()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt
deleted file mode 100644
index 6a801e0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.systemui.unfold.domain.interactor
-
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
-import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
-import com.google.common.truth.Truth.assertThat
-import java.util.Optional
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.async
-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.MockitoAnnotations
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-open class UnfoldTransitionInteractorTest : SysuiTestCase() {
-
- private val testScope = TestScope()
-
- private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider()
- private val unfoldTransitionRepository =
- UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
-
- private lateinit var underTest: UnfoldTransitionInteractor
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- underTest = UnfoldTransitionInteractorImpl(unfoldTransitionRepository)
- }
-
- @Test
- fun waitForTransitionFinish_noEvents_doesNotComplete() =
- testScope.runTest {
- val deferred = async { underTest.waitForTransitionFinish() }
-
- runCurrent()
-
- assertThat(deferred.isCompleted).isFalse()
- deferred.cancel()
- }
-
- @Test
- fun waitForTransitionFinish_finishEvent_completes() =
- testScope.runTest {
- val deferred = async { underTest.waitForTransitionFinish() }
-
- runCurrent()
- unfoldTransitionProgressProvider.onTransitionFinished()
- runCurrent()
-
- assertThat(deferred.isCompleted).isTrue()
- deferred.cancel()
- }
-
- @Test
- fun waitForTransitionFinish_otherEvent_doesNotComplete() =
- testScope.runTest {
- val deferred = async { underTest.waitForTransitionFinish() }
-
- runCurrent()
- unfoldTransitionProgressProvider.onTransitionStarted()
- runCurrent()
-
- assertThat(deferred.isCompleted).isFalse()
- deferred.cancel()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
index 2bc05fc..e5f619b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
@@ -23,7 +23,7 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
import com.android.systemui.utils.os.FakeHandler
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
@@ -34,7 +34,7 @@
@RunWithLooper(setAsMainLooper = true)
class MainThreadUnfoldTransitionProgressProviderTest : SysuiTestCase() {
- private val wrappedProgressProvider = TestUnfoldTransitionProvider()
+ private val wrappedProgressProvider = FakeUnfoldTransitionProvider()
private val fakeHandler = FakeHandler(Looper.getMainLooper())
private val listener = TestUnfoldProgressListener()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
index d864d53..70ec050 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
@@ -20,7 +20,7 @@
import android.view.Surface
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
@@ -43,14 +43,14 @@
@Mock lateinit var rotationChangeProvider: RotationChangeProvider
- private val sourceProvider = TestUnfoldTransitionProvider()
+ private val sourceProvider = FakeUnfoldTransitionProvider()
@Mock lateinit var transitionListener: TransitionProgressListener
@Captor private lateinit var rotationListenerCaptor: ArgumentCaptor<RotationListener>
lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
- private lateinit var testableLooper : TestableLooper
+ private lateinit var testableLooper: TestableLooper
@Before
fun setUp() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
index 2f29b3b..451bd24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
@@ -22,7 +22,7 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.util.mockito.any
import org.junit.Before
@@ -42,7 +42,7 @@
@Mock lateinit var sinkProvider: TransitionProgressListener
- private val sourceProvider = TestUnfoldTransitionProvider()
+ private val sourceProvider = FakeUnfoldTransitionProvider()
private lateinit var contentResolver: ContentResolver
private lateinit var progressProvider: ScaleAwareTransitionProgressProvider
@@ -132,6 +132,6 @@
durationScale.toString()
)
- animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false)
+ animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */ false)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
index 95c934e..4486402 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
@@ -23,7 +23,7 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
import com.android.systemui.unfold.progress.TestUnfoldProgressListener
import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.seconds
@@ -43,7 +43,7 @@
@RunWithLooper
class ScopedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
- private val rootProvider = TestUnfoldTransitionProvider()
+ private val rootProvider = FakeUnfoldTransitionProvider()
private val listener = TestUnfoldProgressListener()
private val testScope = TestScope(UnconfinedTestDispatcher())
private val bgThread =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
index f484ea0..cd4d7b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
@@ -19,7 +19,7 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
import com.android.systemui.unfold.progress.TestUnfoldProgressListener
import com.google.common.util.concurrent.MoreExecutors
import org.junit.Before
@@ -32,7 +32,7 @@
class UnfoldOnlyProgressProviderTest : SysuiTestCase() {
private val listener = TestUnfoldProgressListener()
- private val sourceProvider = TestUnfoldTransitionProvider()
+ private val sourceProvider = FakeUnfoldTransitionProvider()
private val foldProvider = TestFoldProvider()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java
index fb82b8f..483dc0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java
@@ -15,14 +15,14 @@
*/
package com.android.systemui.volume;
+import static com.google.common.truth.Truth.assertThat;
+
import android.media.MediaMetadata;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import junit.framework.Assert;
-
import org.junit.Test;
@SmallTest
@@ -30,11 +30,59 @@
@Test
public void testMediaMetadataToString_null() {
- Assert.assertEquals(null, Util.mediaMetadataToString(null));
+ assertThat(Util.mediaMetadataToString(null)).isNull();
}
@Test
public void testMediaMetadataToString_notNull() {
- Assert.assertNotNull(Util.mediaMetadataToString(new MediaMetadata.Builder().build()));
+ assertThat(Util.mediaMetadataToString(new MediaMetadata.Builder().build())).isNotNull();
+ }
+
+ @Test
+ public void translateToRange_translatesStartToStart() {
+ assertThat(
+ (int) Util.translateToRange(
+ /* value= */ 0,
+ /* valueRangeStart= */ 0,
+ /* valueRangeEnd= */ 7,
+ /* targetRangeStart= */ 0,
+ /* targetRangeEnd= */700)
+ ).isEqualTo(0);
+ }
+
+ @Test
+ public void translateToRange_translatesValueToValue() {
+ assertThat(
+ (int) Util.translateToRange(
+ /* value= */ 4,
+ /* valueRangeStart= */ 0,
+ /* valueRangeEnd= */ 7,
+ /* targetRangeStart= */ 0,
+ /* targetRangeEnd= */700)
+ ).isEqualTo(400);
+ }
+
+ @Test
+ public void translateToRange_translatesEndToEnd() {
+ assertThat(
+ (int) Util.translateToRange(
+ /* value= */ 7,
+ /* valueRangeStart= */ 0,
+ /* valueRangeEnd= */ 7,
+ /* targetRangeStart= */ 0,
+ /* targetRangeEnd= */700)
+ ).isEqualTo(700);
+ }
+
+ @Test
+ public void translateToRange_returnsStartForEmptyRange() {
+ assertThat(
+ (int) Util.translateToRange(
+ /* value= */ 7,
+ /* valueRangeStart= */ 7,
+ /* valueRangeEnd= */ 7,
+ /* targetRangeStart= */ 700,
+ /* targetRangeEnd= */700)
+ ).isEqualTo(700);
}
}
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 11a53f7..3b468aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -65,6 +65,7 @@
import android.widget.SeekBar;
import androidx.test.core.view.MotionEventBuilder;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.internal.jank.InteractionJankMonitor;
@@ -247,6 +248,8 @@
VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState();
ss.name = STREAMS.get(i);
ss.level = 1;
+ ss.levelMin = 0;
+ ss.levelMax = 25;
state.states.append(i, ss);
}
return state;
@@ -293,7 +296,7 @@
mTestableLooper.processAllMessages();
}
- @Test
+ @Test @FlakyTest(bugId = 329099861)
@EnableFlags(FLAG_HAPTIC_VOLUME_SLIDER)
public void testVolumeChange_withSliderHaptics_deliversOnProgressChangedHapticsEagerly() {
// create haptic plugins on the rows with the flag enabled
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 aabd4e9..d9a0c4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -121,8 +121,6 @@
import com.android.systemui.scene.FakeWindowRootViewComponent;
import com.android.systemui.scene.data.repository.SceneContainerRepository;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
-import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.scene.shared.logger.SceneLogger;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
@@ -174,6 +172,7 @@
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
import com.android.systemui.util.FakeEventLog;
import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -359,8 +358,6 @@
@Mock
private Display mDefaultDisplay;
@Mock
- private SceneContainerFlags mSceneContainerFlags;
- @Mock
private LargeScreenHeaderHelper mLargeScreenHeaderHelper;
private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
@@ -429,14 +426,12 @@
mock(SceneLogger.class),
mKosmos.getDeviceUnlockedInteractor());
- FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags();
KeyguardTransitionInteractor keyguardTransitionInteractor =
mKosmos.getKeyguardTransitionInteractor();
KeyguardInteractor keyguardInteractor = new KeyguardInteractor(
keyguardRepository,
new FakeCommandQueue(),
powerInteractor,
- sceneContainerFlags,
new FakeKeyguardBouncerRepository(),
new ConfigurationInteractor(configurationRepository),
shadeRepository,
@@ -502,7 +497,6 @@
mShadeWindowLogger,
() -> mSelectedUserInteractor,
mUserTracker,
- mSceneContainerFlags,
mKosmos::getCommunalInteractor
);
mNotificationShadeWindowController.fetchWindowRootView();
@@ -554,7 +548,8 @@
mock(SystemClock.class),
mock(UiEventLogger.class),
mock(UserTracker.class),
- mock(AvalancheProvider.class)
+ mock(AvalancheProvider.class),
+ mock(SystemSettings.class)
);
interruptionDecisionProvider.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
deleted file mode 100644
index d2c8aea..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ /dev/null
@@ -1,141 +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.systemui.wmshell;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.notetask.NoteTaskInitializer;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.desktopmode.DesktopMode;
-import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
-import com.android.wm.shell.onehanded.OneHanded;
-import com.android.wm.shell.onehanded.OneHandedEventCallback;
-import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
-import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.recents.RecentTasks;
-import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.sysui.ShellInterface;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-/**
- * Tests for {@link WMShell}.
- *
- * Build/Install/Run:
- * atest SystemUITests:WMShellTest
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class WMShellTest extends SysuiTestCase {
- WMShell mWMShell;
-
- @Mock ShellInterface mShellInterface;
- @Mock CommandQueue mCommandQueue;
- @Mock ConfigurationController mConfigurationController;
- @Mock KeyguardStateController mKeyguardStateController;
- @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock ScreenLifecycle mScreenLifecycle;
- @Mock SysUiState mSysUiState;
- @Mock Pip mPip;
- @Mock SplitScreen mSplitScreen;
- @Mock OneHanded mOneHanded;
- @Mock WakefulnessLifecycle mWakefulnessLifecycle;
- @Mock UserTracker mUserTracker;
- @Mock ShellExecutor mSysUiMainExecutor;
- @Mock NoteTaskInitializer mNoteTaskInitializer;
- @Mock DesktopMode mDesktopMode;
- @Mock RecentTasks mRecentTasks;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext);
- mWMShell = new WMShell(
- mContext,
- mShellInterface,
- Optional.of(mPip),
- Optional.of(mSplitScreen),
- Optional.of(mOneHanded),
- Optional.of(mDesktopMode),
- Optional.of(mRecentTasks),
- mCommandQueue,
- mConfigurationController,
- mKeyguardStateController,
- mKeyguardUpdateMonitor,
- mScreenLifecycle,
- mSysUiState,
- mWakefulnessLifecycle,
- mUserTracker,
- displayTracker,
- mNoteTaskInitializer,
- mSysUiMainExecutor
- );
- }
-
- @Test
- public void initPip_registersCommandQueueCallback() {
- mWMShell.initPip(mPip);
-
- verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
- }
-
- @Test
- public void initOneHanded_registersCallbacks() {
- mWMShell.initOneHanded(mOneHanded);
-
- verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
- verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
- verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class));
- verify(mOneHanded).registerEventCallback(any(OneHandedEventCallback.class));
- }
-
- @Test
- public void initDesktopMode_registersListener() {
- mWMShell.initDesktopMode(mDesktopMode);
- verify(mDesktopMode).addVisibleTasksListener(
- any(DesktopModeTaskRepository.VisibleTasksListener.class),
- any(Executor.class));
- }
-
- @Test
- public void initRecentTasks_registersListener() {
- mWMShell.initRecentTasks(mRecentTasks);
- verify(mRecentTasks).addAnimationStateListener(any(Executor.class), any());
- }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/ActivityIntentHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/ActivityIntentHelperKosmos.kt
index 7185b7c..96c4c45 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/ActivityIntentHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/ActivityIntentHelperKosmos.kt
@@ -18,5 +18,7 @@
import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
-val Kosmos.activityIntentHelper by Kosmos.Fixture { ActivityIntentHelper(applicationContext) }
+val Kosmos.mockActivityIntentHelper by Kosmos.Fixture { mock<ActivityIntentHelper>() }
+var Kosmos.activityIntentHelper by Kosmos.Fixture { ActivityIntentHelper(applicationContext) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
index de7b14d..0682361 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
@@ -31,7 +31,7 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.SystemUIDeviceEntryFaceAuthInteractor
import com.android.systemui.scene.SceneContainerFrameworkModule
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSource
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
@@ -104,11 +104,10 @@
@Provides
fun provideBaseShadeInteractor(
- sceneContainerFlags: SceneContainerFlags,
sceneContainerOn: Provider<ShadeInteractorSceneContainerImpl>,
sceneContainerOff: Provider<ShadeInteractorLegacyImpl>
): BaseShadeInteractor {
- return if (sceneContainerFlags.isEnabled()) {
+ return if (SceneContainerFlag.isEnabled) {
sceneContainerOn.get()
} else {
sceneContainerOff.get()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/FakeReduceBrightColorsController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/FakeReduceBrightColorsController.kt
new file mode 100644
index 0000000..8b0affe2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/FakeReduceBrightColorsController.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility
+
+import com.android.systemui.qs.ReduceBrightColorsController
+
+class FakeReduceBrightColorsController : ReduceBrightColorsController {
+
+ private var isEnabled = false
+
+ private val callbacks = LinkedHashSet<ReduceBrightColorsController.Listener>()
+
+ override fun addCallback(listener: ReduceBrightColorsController.Listener) {
+ callbacks.add(listener)
+ }
+
+ override fun removeCallback(listener: ReduceBrightColorsController.Listener) {
+ callbacks.remove(listener)
+ }
+
+ override fun isReduceBrightColorsActivated(): Boolean {
+ return isEnabled
+ }
+
+ override fun setReduceBrightColorsActivated(activated: Boolean) {
+ if (activated != isEnabled) {
+ isEnabled = activated
+ for (callback in callbacks) {
+ callback.onActivated(activated)
+ }
+ }
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/ReduceBrightColorsControllerKosmos.kt
similarity index 61%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/ReduceBrightColorsControllerKosmos.kt
index f6140f5..5bbe3bf 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/ReduceBrightColorsControllerKosmos.kt
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.accessibility
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.ReduceBrightColorsController
+
+var Kosmos.reduceBrightColorsController: ReduceBrightColorsController by
+ Kosmos.Fixture { fakeReduceBrightColorsController }
+val Kosmos.fakeReduceBrightColorsController by Kosmos.Fixture { FakeReduceBrightColorsController() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index a6dd3cd..219794f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -32,6 +32,8 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.currentTime
@@ -68,6 +70,8 @@
var lockoutStartedReportCount = 0
+ private val credentialCheckingMutex = Mutex(locked = false)
+
override suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
return authenticationMethod.value
}
@@ -124,30 +128,32 @@
override suspend fun checkCredential(
credential: LockscreenCredential
): AuthenticationResultModel {
- val expectedCredential = credentialOverride ?: getExpectedCredential(securityMode)
- val isSuccessful =
- when {
- credential.type != getCurrentCredentialType(securityMode) -> false
- credential.type == LockPatternUtils.CREDENTIAL_TYPE_PIN ->
- credential.isPin && credential.matches(expectedCredential)
- credential.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ->
- credential.isPassword && credential.matches(expectedCredential)
- credential.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN ->
- credential.isPattern && credential.matches(expectedCredential)
- else -> error("Unexpected credential type ${credential.type}!")
- }
+ return credentialCheckingMutex.withLock {
+ val expectedCredential = credentialOverride ?: getExpectedCredential(securityMode)
+ val isSuccessful =
+ when {
+ credential.type != getCurrentCredentialType(securityMode) -> false
+ credential.type == LockPatternUtils.CREDENTIAL_TYPE_PIN ->
+ credential.isPin && credential.matches(expectedCredential)
+ credential.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ->
+ credential.isPassword && credential.matches(expectedCredential)
+ credential.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN ->
+ credential.isPattern && credential.matches(expectedCredential)
+ else -> error("Unexpected credential type ${credential.type}!")
+ }
- val failedAttempts = _failedAuthenticationAttempts.value
- return if (isSuccessful || failedAttempts < MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) {
- AuthenticationResultModel(
- isSuccessful = isSuccessful,
- lockoutDurationMs = 0,
- )
- } else {
- AuthenticationResultModel(
- isSuccessful = false,
- lockoutDurationMs = LOCKOUT_DURATION_MS,
- )
+ val failedAttempts = _failedAuthenticationAttempts.value
+ if (isSuccessful || failedAttempts < MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) {
+ AuthenticationResultModel(
+ isSuccessful = isSuccessful,
+ lockoutDurationMs = 0,
+ )
+ } else {
+ AuthenticationResultModel(
+ isSuccessful = false,
+ lockoutDurationMs = LOCKOUT_DURATION_MS,
+ )
+ }
}
}
@@ -155,6 +161,23 @@
_isPinEnhancedPrivacyEnabled.value = isEnabled
}
+ /**
+ * Pauses any future credential checking. The test must call [unpauseCredentialChecking] to
+ * flush the accumulated credential checks.
+ */
+ suspend fun pauseCredentialChecking() {
+ credentialCheckingMutex.lock()
+ }
+
+ /**
+ * Unpauses future credential checking, if it was paused using [pauseCredentialChecking]. This
+ * doesn't flush any pending coroutine jobs; the test code may still choose to do that using
+ * `runCurrent`.
+ */
+ fun unpauseCredentialChecking() {
+ credentialCheckingMutex.unlock()
+ }
+
private fun getExpectedCredential(securityMode: SecurityMode): List<Any> {
return when (val credentialType = getCurrentCredentialType(securityMode)) {
LockPatternUtils.CREDENTIAL_TYPE_PIN -> credentialOverride ?: DEFAULT_PIN
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
index 9cbe633..2ae6f542 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.ui.viewmodel
import android.content.applicationContext
+import com.android.systemui.biometrics.domain.interactor.biometricStatusInteractor
import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
@@ -30,6 +31,7 @@
promptSelectorInteractor = promptSelectorInteractor,
context = applicationContext,
udfpsOverlayInteractor = udfpsOverlayInteractor,
+ biometricStatusInteractor = biometricStatusInteractor,
udfpsUtils = udfpsUtils
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/BroadcastDialogControllerKosmos.kt
similarity index 72%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/BroadcastDialogControllerKosmos.kt
index f6140f5..e9d7266 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/BroadcastDialogControllerKosmos.kt
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.bluetooth
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.mockBroadcastDialogController by Kosmos.Fixture { mock<BroadcastDialogController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorKosmos.kt
index c065545..4a02f6d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.bouncer.domain.interactor
+import com.android.internal.logging.uiEventLogger
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.bouncer.data.repository.bouncerRepository
import com.android.systemui.classifier.domain.interactor.falsingInteractor
@@ -23,7 +24,9 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.sessionTracker
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
val Kosmos.bouncerInteractor by Fixture {
BouncerInteractor(
@@ -33,5 +36,8 @@
deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
falsingInteractor = falsingInteractor,
powerInteractor = powerInteractor,
+ uiEventLogger = uiEventLogger,
+ sessionTracker = sessionTracker,
+ sceneInteractor = sceneInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
index 5c3e1f4..60d97d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
@@ -17,8 +17,6 @@
package com.android.systemui.bouncer.shared.flag
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-var Kosmos.fakeComposeBouncerFlags by
- Kosmos.Fixture { FakeComposeBouncerFlags(fakeSceneContainerFlags) }
+var Kosmos.fakeComposeBouncerFlags by Kosmos.Fixture { FakeComposeBouncerFlags() }
val Kosmos.composeBouncerFlags by Kosmos.Fixture<ComposeBouncerFlags> { fakeComposeBouncerFlags }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt
index c116bbd..7482c0f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt
@@ -16,14 +16,11 @@
package com.android.systemui.bouncer.shared.flag
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
-class FakeComposeBouncerFlags(
- private val sceneContainerFlags: SceneContainerFlags,
- var composeBouncerEnabled: Boolean = false
-) : ComposeBouncerFlags {
+class FakeComposeBouncerFlags(var composeBouncerEnabled: Boolean = false) : ComposeBouncerFlags {
override fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
- return sceneContainerFlags.isEnabled() || composeBouncerEnabled
+ return SceneContainerFlag.isEnabled || composeBouncerEnabled
}
@Deprecated(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
index 0f6c7cf..c3dad74 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
@@ -18,6 +18,7 @@
package com.android.systemui.bouncer.ui.viewmodel
+import android.app.admin.devicePolicyManager
import android.content.applicationContext
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
@@ -31,7 +32,6 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.user.domain.interactor.selectedUserInteractor
import com.android.systemui.user.ui.viewmodel.userSwitcherViewModel
-import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.bouncerViewModel by Fixture {
@@ -44,12 +44,12 @@
simBouncerInteractor = simBouncerInteractor,
authenticationInteractor = authenticationInteractor,
selectedUserInteractor = selectedUserInteractor,
+ devicePolicyManager = devicePolicyManager,
+ bouncerMessageViewModel = bouncerMessageViewModel,
flags = composeBouncerFlags,
selectedUser = userSwitcherViewModel.selectedUser,
users = userSwitcherViewModel.users,
userSwitcherMenu = userSwitcherViewModel.menu,
actionButton = bouncerActionButtonInteractor.actionButton,
- devicePolicyManager = mock(),
- bouncerMessageViewModel = bouncerMessageViewModel,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 8866fd3..3dd382f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -23,6 +23,7 @@
import com.android.systemui.communal.data.repository.communalRepository
import com.android.systemui.communal.data.repository.communalWidgetRepository
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
+import com.android.systemui.dock.fakeDockManager
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -33,7 +34,6 @@
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.activityStarter
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.settings.userTracker
import com.android.systemui.smartspace.data.repository.smartspaceRepository
import com.android.systemui.user.data.repository.fakeUserRepository
@@ -45,20 +45,20 @@
broadcastDispatcher = broadcastDispatcher,
communalRepository = communalRepository,
widgetRepository = communalWidgetRepository,
- mediaRepository = communalMediaRepository,
communalPrefsRepository = communalPrefsRepository,
+ mediaRepository = communalMediaRepository,
smartspaceRepository = smartspaceRepository,
- appWidgetHost = mock(),
keyguardInteractor = keyguardInteractor,
+ communalSettingsInteractor = communalSettingsInteractor,
+ appWidgetHost = mock(),
editWidgetsActivityStarter = editWidgetsActivityStarter,
userTracker = userTracker,
activityStarter = activityStarter,
userManager = userManager,
+ dockManager = fakeDockManager,
+ sceneInteractor = sceneInteractor,
logBuffer = logcatLogBuffer("CommunalInteractor"),
tableLogBuffer = mock(),
- communalSettingsInteractor = communalSettingsInteractor,
- sceneInteractor = sceneInteractor,
- sceneContainerFlags = fakeSceneContainerFlags,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
index b4773f6..cd2710e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
@@ -17,17 +17,21 @@
package com.android.systemui.communal.domain.interactor
import com.android.systemui.communal.data.repository.communalSettingsRepository
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.settings.userTracker
import com.android.systemui.user.domain.interactor.selectedUserInteractor
import com.android.systemui.util.mockito.mock
val Kosmos.communalSettingsInteractor by Fixture {
CommunalSettingsInteractor(
bgScope = applicationCoroutineScope,
+ bgExecutor = fakeExecutor,
repository = communalSettingsRepository,
userInteractor = selectedUserInteractor,
+ userTracker = userTracker,
tableLogBuffer = mock(),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
index 2396722..e3c218d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
@@ -16,6 +16,9 @@
package com.android.systemui.communal.ui.viewmodel
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.util.communalColors
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.viewmodel.dreamingToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToDreamingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTransitionViewModel
@@ -27,9 +30,14 @@
val Kosmos.communalTransitionViewModel by
Kosmos.Fixture {
CommunalTransitionViewModel(
- glanceableHubToLockscreenTransitionViewModel,
- lockscreenToGlanceableHubTransitionViewModel,
- dreamingToGlanceableHubTransitionViewModel,
- glanceableHubToDreamingTransitionViewModel,
+ glanceableHubToLockscreenTransitionViewModel =
+ glanceableHubToLockscreenTransitionViewModel,
+ lockscreenToGlanceableHubTransitionViewModel =
+ lockscreenToGlanceableHubTransitionViewModel,
+ dreamToGlanceableHubTransitionViewModel = dreamingToGlanceableHubTransitionViewModel,
+ glanceableHubToDreamTransitionViewModel = glanceableHubToDreamingTransitionViewModel,
+ communalInteractor = communalInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
+ communalColors = communalColors,
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/util/CommunalColorsKosmos.kt
similarity index 71%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/communal/util/CommunalColorsKosmos.kt
index f6140f5..e76cf68 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/util/CommunalColorsKosmos.kt
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.communal.util
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.communalColors: CommunalColors by Kosmos.Fixture { fakeCommunalColors }
+val Kosmos.fakeCommunalColors by Kosmos.Fixture { FakeCommunalColors() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/util/FakeCommunalColors.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/util/FakeCommunalColors.kt
new file mode 100644
index 0000000..7046658
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/util/FakeCommunalColors.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.util
+
+import android.graphics.Color
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeCommunalColors : CommunalColors {
+ private val _backgroundColor = MutableStateFlow(Color.valueOf(Color.BLACK))
+
+ override val backgroundColor: StateFlow<Color>
+ get() = _backgroundColor.asStateFlow()
+
+ fun setBackgroundColor(color: Color) {
+ _backgroundColor.value = color
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/tests/utils/src/com/android/systemui/dock/DockManagerFake.java
similarity index 94%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFake.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/dock/DockManagerFake.java
index 3754062..b99310b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFake.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dock/DockManagerFake.java
@@ -49,6 +49,7 @@
return mDocked;
}
+ /** Sets the docked state */
public void setIsDocked(boolean docked) {
mDocked = docked;
}
@@ -58,6 +59,7 @@
return false;
}
+ /** Notifies callbacks of dock state change */
public void setDockEvent(int event) {
mCallback.onEvent(event);
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFakeKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dock/DockManagerFakeKosmos.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFakeKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/dock/DockManagerFakeKosmos.kt
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/BrokenWithSceneContainer.kt
similarity index 61%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/flags/BrokenWithSceneContainer.kt
index f6140f5..29b088b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/BrokenWithSceneContainer.kt
@@ -14,9 +14,14 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.flags
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
+
+/**
+ * This is used by [SceneContainerRule] to assert that the test is broken when
+ * [FLAG_SCENE_CONTAINER] is enabled.
+ */
+@Retention(AnnotationRetention.RUNTIME)
+@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
+annotation class BrokenWithSceneContainer(val bugId: Int)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/DisableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/DisableSceneContainer.kt
new file mode 100644
index 0000000..09f3430
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/DisableSceneContainer.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import android.platform.test.annotations.DisableFlags
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
+
+/**
+ * This includes @[DisableFlags] to work with [SetFlagsRule] to disable all aconfig flags required
+ * by that feature.
+ */
+@DisableFlags(
+ FLAG_SCENE_CONTAINER,
+)
+@Retention(AnnotationRetention.RUNTIME)
+@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
+annotation class DisableSceneContainer
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
index c1d2ad6..e83205c5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -18,12 +18,14 @@
import android.platform.test.annotations.EnableFlags
import com.android.systemui.Flags.FLAG_COMPOSE_LOCKSCREEN
+import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.Flags.FLAG_MEDIA_IN_SCENE_CONTAINER
import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.Flags.FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR
import com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_SYSUI
+import com.android.systemui.Flags.FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
/**
@@ -39,6 +41,8 @@
FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR,
FLAG_PREDICTIVE_BACK_SYSUI,
FLAG_SCENE_CONTAINER,
+ FLAG_DEVICE_ENTRY_UDFPS_REFACTOR,
+ FLAG_REFACTOR_KEYGUARD_DISMISS_INTENT,
)
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
index d6f2f77..45ea364 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
@@ -34,8 +34,9 @@
Kosmos.Fixture {
FakeFeatureFlagsClassic().apply {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- set(Flags.NSSL_DEBUG_LINES, false)
set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false)
+ set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
+ set(Flags.NSSL_DEBUG_LINES, false)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerFlagParameterization.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerFlagParameterization.kt
new file mode 100644
index 0000000..4e24233
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerFlagParameterization.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
+
+/** The name of the one flag to be disabled for OFF parameterization */
+private const val flagNameToDisable = FLAG_SCENE_CONTAINER
+
+/** Cache of the flags to be enabled for ON parameterization */
+private val flagNamesToEnable =
+ EnableSceneContainer::class.java.getAnnotation(EnableFlags::class.java)!!.value.toList()
+
+/**
+ * Provides one or two copies of this [FlagsParameterization]; one which disabled
+ * [FLAG_SCENE_CONTAINER] and if none of the dependencies of it are disabled by this, a second copy
+ * which enables [FLAG_SCENE_CONTAINER] and all the dependencies (just like [EnableSceneContainer]).
+ */
+fun FlagsParameterization.andSceneContainer(): Sequence<FlagsParameterization> = sequence {
+ check(flagNameToDisable !in mOverrides) {
+ "Can't add $flagNameToDisable to FlagsParameterization: $this"
+ }
+ yield(FlagsParameterization(mOverrides + mapOf(flagNameToDisable to false)))
+ if (flagNamesToEnable.all { mOverrides[it] != false }) {
+ // Can't add the parameterization of enabling SceneContainerFlag to a parameterization that
+ // explicitly disables one of the prerequisite flags.
+ yield(FlagsParameterization(mOverrides + flagNamesToEnable.associateWith { true }))
+ }
+}
+
+/**
+ * Doubles (roughly; see below) the given list of [FlagsParameterization] for enabling and disabling
+ * SceneContainerFlag.
+ *
+ * The input parameterization may not define [FLAG_SCENE_CONTAINER].
+ *
+ * Any [FlagsParameterization] which disables any flag that is a dependency of
+ * [FLAG_SCENE_CONTAINER], will not add a state for enabling, and the state will simply be converted
+ * to one which disables. Just like [EnableSceneContainer], enabling will also enable all the other
+ * dependencies. For any flag parameterization where a dependency is disabled, an "enabled"
+ * parameterization is inconsistent, so it will not be added.
+ */
+fun List<FlagsParameterization>.andSceneContainer(): List<FlagsParameterization> =
+ flatMap { it.andSceneContainer() }.toList()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
index 775ad14..9ec1481 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
@@ -18,6 +18,7 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import org.junit.Assert
+import org.junit.AssumptionViolatedException
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
@@ -33,10 +34,7 @@
return object : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
- val hasAnnotation =
- description?.testClass?.getAnnotation(EnableSceneContainer::class.java) !=
- null || description?.getAnnotation(EnableSceneContainer::class.java) != null
- if (hasAnnotation) {
+ if (description.hasAnnotation<EnableSceneContainer>()) {
Assert.assertTrue(
"SceneContainerFlag.isEnabled is false:" +
"\n * Did you forget to add a new aconfig flag dependency in" +
@@ -45,8 +43,31 @@
SceneContainerFlag.isEnabled
)
}
+ if (
+ description.hasAnnotation<BrokenWithSceneContainer>() &&
+ SceneContainerFlag.isEnabled
+ ) {
+ runCatching { base?.evaluate() }
+ .onFailure { exception ->
+ if (exception is AssumptionViolatedException) {
+ throw AssertionError(
+ "This is marked @BrokenWithSceneContainer, but was skipped.",
+ exception
+ )
+ }
+ throw AssumptionViolatedException("Test is still broken", exception)
+ }
+ throw AssertionError(
+ "HOORAY! You fixed a test that was marked @BrokenWithSceneContainer. " +
+ "Remove the obsolete annotation to fix this failure."
+ )
+ }
base?.evaluate()
}
}
}
+
+ inline fun <reified T : Annotation> Description?.hasAnnotation(): Boolean =
+ this?.testClass?.getAnnotation(T::class.java) != null ||
+ this?.getAnnotation(T::class.java) != null
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
similarity index 61%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
index f6140f5..636d509 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.haptics.qs
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import com.android.systemui.haptics.vibratorHelper
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+
+val Kosmos.qsLongPressEffect by
+ Kosmos.Fixture { QSLongPressEffect(vibratorHelper, keyguardInteractor, testScope) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt
index eba5a11..4f2310f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt
@@ -50,14 +50,25 @@
get() = _previewClock
override val clockEventController: ClockEventController
get() = mock()
+ override val shouldForceSmallClock: Boolean
+ get() = _shouldForceSmallClock
+ private var _shouldForceSmallClock: Boolean = false
override fun setClockSize(@ClockSize size: Int) {
_clockSize.value = size
}
+ fun setSelectedClockSize(size: SettingsClockSize) {
+ selectedClockSize.value = size
+ }
+
fun setCurrentClock(clockController: ClockController) {
_currentClock.value = clockController
}
+
+ fun setShouldForceSmallClock(shouldForceSmallClock: Boolean) {
+ _shouldForceSmallClock = shouldForceSmallClock
+ }
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
index 75489b6..8954231 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
@@ -17,8 +17,6 @@
package com.android.systemui.keyguard.data.repository
import android.os.fakeExecutorHandler
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.WEATHER_CLOCK_BLUEPRINT_ID
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
@@ -34,8 +32,6 @@
setOf(
defaultBlueprint,
splitShadeBlueprint,
- weatherClockBlueprint,
- splitShadeWeatherClockBlueprint,
),
handler = fakeExecutorHandler,
assert = mock<ThreadAssert>(),
@@ -50,22 +46,6 @@
get() = listOf()
}
-private val weatherClockBlueprint =
- object : KeyguardBlueprint {
- override val id: String
- get() = WEATHER_CLOCK_BLUEPRINT_ID
- override val sections: List<KeyguardSection>
- get() = listOf()
- }
-
-private val splitShadeWeatherClockBlueprint =
- object : KeyguardBlueprint {
- override val id: String
- get() = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
- override val sections: List<KeyguardSection>
- get() = listOf()
- }
-
private val splitShadeBlueprint =
object : KeyguardBlueprint {
override val id: String
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorKosmos.kt
index 12165cd..d52883e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorKosmos.kt
@@ -18,6 +18,22 @@
import com.android.systemui.keyguard.data.repository.keyguardClockRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
val Kosmos.keyguardClockInteractor by
- Kosmos.Fixture { KeyguardClockInteractor(keyguardClockRepository) }
+ Kosmos.Fixture {
+ KeyguardClockInteractor(
+ keyguardClockRepository = keyguardClockRepository,
+ applicationScope = applicationCoroutineScope,
+ mediaCarouselInteractor = mediaCarouselInteractor,
+ activeNotificationsInteractor = activeNotificationsInteractor,
+ shadeInteractor = shadeInteractor,
+ keyguardInteractor = keyguardInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
+ headsUpNotificationInteractor = headsUpNotificationInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
new file mode 100644
index 0000000..2c6d44f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+val Kosmos.keyguardDismissActionInteractor by
+ Kosmos.Fixture {
+ KeyguardDismissActionInteractor(
+ repository = keyguardRepository,
+ transitionInteractor = keyguardTransitionInteractor,
+ dismissInteractor = keyguardDismissInteractor,
+ applicationScope = testScope.backgroundScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
new file mode 100644
index 0000000..f33ca95
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.data.repository.trustRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+val Kosmos.keyguardDismissInteractor by
+ Kosmos.Fixture {
+ KeyguardDismissInteractor(
+ trustRepository = trustRepository,
+ keyguardRepository = keyguardRepository,
+ primaryBouncerInteractor = primaryBouncerInteractor,
+ alternateBouncerInteractor = alternateBouncerInteractor,
+ powerInteractor = powerInteractor,
+ selectedUserInteractor = selectedUserInteractor,
+ )
+ }
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 e21c766..2e751cc 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
@@ -24,12 +24,9 @@
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor.ConfigurationBasedDimensions
@@ -49,7 +46,6 @@
@JvmStatic
fun create(
featureFlags: FakeFeatureFlags = FakeFeatureFlags(),
- sceneContainerFlags: SceneContainerFlags = FakeSceneContainerFlags(),
repository: FakeKeyguardRepository = FakeKeyguardRepository(),
commandQueue: FakeCommandQueue = FakeCommandQueue(),
bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
@@ -88,7 +84,6 @@
repository = repository,
commandQueue = commandQueue,
featureFlags = featureFlags,
- sceneContainerFlags = sceneContainerFlags,
bouncerRepository = bouncerRepository,
configurationRepository = configurationRepository,
shadeRepository = shadeRepository,
@@ -96,13 +91,12 @@
KeyguardInteractor(
repository = repository,
commandQueue = commandQueue,
- sceneContainerFlags = sceneContainerFlags,
+ powerInteractor = powerInteractor,
bouncerRepository = bouncerRepository,
configurationInteractor = ConfigurationInteractor(configurationRepository),
shadeRepository = shadeRepository,
- sceneInteractorProvider = { sceneInteractor },
keyguardTransitionInteractor = keyguardTransitionInteractor,
- powerInteractor = powerInteractor,
+ sceneInteractorProvider = { sceneInteractor },
fromGoneTransitionInteractor = { fromGoneTransitionInteractor },
sharedNotificationContainerInteractor = { sncInteractor },
applicationScope = testScope,
@@ -114,7 +108,6 @@
val repository: FakeKeyguardRepository,
val commandQueue: FakeCommandQueue,
val featureFlags: FakeFeatureFlags,
- val sceneContainerFlags: SceneContainerFlags,
val bouncerRepository: FakeKeyguardBouncerRepository,
val configurationRepository: FakeConfigurationRepository,
val shadeRepository: FakeShadeRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
index 2a0c01c..9426718 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
@@ -23,7 +23,6 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.statusbar.commandQueue
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
@@ -34,7 +33,6 @@
repository = keyguardRepository,
commandQueue = commandQueue,
powerInteractor = powerInteractor,
- sceneContainerFlags = sceneContainerFlags,
bouncerRepository = keyguardBouncerRepository,
configurationInteractor = configurationInteractor,
shadeRepository = shadeRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
index 185deda..6cc1e8e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
@@ -20,13 +20,11 @@
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.kosmos.testDispatcher
val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by
Kosmos.Fixture {
KeyguardTransitionInteractor(
scope = applicationCoroutineScope,
- mainDispatcher = testDispatcher,
repository = keyguardTransitionRepository,
keyguardRepository = keyguardRepository,
fromLockscreenTransitionInteractor = { fromLockscreenTransitionInteractor },
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt
index f7de5a4..1a05d21 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt
@@ -22,14 +22,10 @@
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.kosmos.testDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.keyguardTransitionAnimationFlow by Fixture {
KeyguardTransitionAnimationFlow(
- scope = applicationCoroutineScope,
- mainDispatcher = testDispatcher,
transitionInteractor = keyguardTransitionInteractor,
logger = keyguardTransitionAnimationLogger,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelKosmos.kt
new file mode 100644
index 0000000..bc35dc8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.accessibilityActionsViewModelKosmos by Fixture {
+ AccessibilityActionsViewModel(
+ communalInteractor = communalInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
+ keyguardInteractor = keyguardInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
index b4f1218..bdd4afa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
@@ -18,7 +18,7 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
@@ -27,6 +27,6 @@
val Kosmos.alternateBouncerViewModel by Fixture {
AlternateBouncerViewModel(
statusBarKeyguardViewManager = statusBarKeyguardViewManager,
- animationFlow = keyguardTransitionAnimationFlow,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt
index ffa4133..9774e4a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt
@@ -19,21 +19,19 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.keyguard.domain.interactor.keyguardDismissActionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.sysuiStatusBarStateController
-import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.bouncerToGoneFlows by Fixture {
BouncerToGoneFlows(
statusBarStateController = sysuiStatusBarStateController,
primaryBouncerInteractor = mockPrimaryBouncerInteractor,
- keyguardDismissActionInteractor = mock(),
- featureFlags = featureFlagsClassic,
+ keyguardDismissActionInteractor = { keyguardDismissActionInteractor },
shadeInteractor = shadeInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
index 709f864..58b0ff8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
@@ -26,7 +26,6 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -47,7 +46,6 @@
transitionInteractor = keyguardTransitionInteractor,
keyguardInteractor = keyguardInteractor,
viewModel = aodToLockscreenTransitionViewModel,
- sceneContainerFlags = sceneContainerFlags,
keyguardViewController = { statusBarKeyguardViewManager },
deviceEntryInteractor = deviceEntryInteractor,
deviceEntrySourceInteractor = deviceEntrySourceInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
index 60dd48a..a048d3c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -26,7 +25,6 @@
val Kosmos.keyguardClockViewModel by
Kosmos.Fixture {
KeyguardClockViewModel(
- keyguardInteractor = keyguardInteractor,
keyguardClockInteractor = keyguardClockInteractor,
applicationScope = applicationCoroutineScope,
notifsKeyguardInteractor = notificationsKeyguardInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
index 1e25f7f..30a4f21 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
@@ -22,6 +22,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
val Kosmos.lockscreenContentViewModel by
Kosmos.Fixture {
@@ -32,5 +33,6 @@
longPress = keyguardLongPressViewModel,
shadeInteractor = shadeInteractor,
applicationScope = applicationCoroutineScope,
+ unfoldTransitionInteractor = unfoldTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModelKosmos.kt
new file mode 100644
index 0000000..3b96912
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModelKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+var Kosmos.occludedToGoneTransitionViewModel by Fixture {
+ OccludedToGoneTransitionViewModel(
+ animationFlow = keyguardTransitionAnimationFlow,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt
index e6651a4..f86e9b7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt
@@ -20,6 +20,8 @@
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -30,5 +32,7 @@
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
configurationInteractor = configurationInteractor,
animationFlow = keyguardTransitionAnimationFlow,
+ keyguardInteractor = keyguardInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt
index 4ecff73..d6edea2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt
@@ -19,20 +19,18 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor
-import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.keyguard.domain.interactor.keyguardDismissActionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.statusbar.sysuiStatusBarStateController
-import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.primaryBouncerToGoneTransitionViewModel by Fixture {
PrimaryBouncerToGoneTransitionViewModel(
statusBarStateController = sysuiStatusBarStateController,
primaryBouncerInteractor = mockPrimaryBouncerInteractor,
- keyguardDismissActionInteractor = mock(),
- featureFlags = featureFlagsClassic,
+ keyguardDismissActionInteractor = { keyguardDismissActionInteractor },
bouncerToGoneFlows = bouncerToGoneFlows,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index afc8f30..162f278 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -33,6 +33,7 @@
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.globalactions.domain.interactor.globalActionsInteractor
+import com.android.systemui.haptics.qs.qsLongPressEffect
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -47,9 +48,9 @@
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.sceneContainerConfig
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.sceneDataSource
import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
+import com.android.systemui.shade.shadeController
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
import com.android.systemui.statusbar.phone.screenOffAnimationController
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
@@ -69,7 +70,6 @@
val testDispatcher by lazy { kosmos.testDispatcher }
val testScope by lazy { kosmos.testScope }
val fakeFeatureFlags by lazy { kosmos.fakeFeatureFlagsClassic }
- val fakeSceneContainerFlags by lazy { kosmos.fakeSceneContainerFlags }
val fakeExecutor by lazy { kosmos.fakeExecutor }
val fakeExecutorHandler by lazy { kosmos.fakeExecutorHandler }
val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
@@ -108,6 +108,8 @@
kosmos.sharedNotificationContainerInteractor
}
val brightnessMirrorShowingInteractor by lazy { kosmos.brightnessMirrorShowingInteractor }
+ val qsLongPressEffect by lazy { kosmos.qsLongPressEffect }
+ val shadeController by lazy { kosmos.shadeController }
init {
kosmos.applicationContext = testCase.context
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/SessionTrackerKosmos.kt
similarity index 68%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/log/SessionTrackerKosmos.kt
index f6140f5..eac9149 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/SessionTrackerKosmos.kt
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.log
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.sessionTracker: SessionTracker by Kosmos.Fixture { sessionTrackerMock }
+val Kosmos.sessionTrackerMock: SessionTracker by Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt
index 7ce810e..7dab5c2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt
@@ -17,5 +17,6 @@
package com.android.systemui.media.controls.data.repository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.time.systemClock
-val Kosmos.mediaFilterRepository by Kosmos.Fixture { MediaFilterRepository() }
+val Kosmos.mediaFilterRepository by Kosmos.Fixture { MediaFilterRepository(systemClock) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
index 29c5bd5..81adefa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
@@ -16,16 +16,30 @@
package com.android.systemui.media.controls.domain.pipeline.interactor
+import android.content.applicationContext
+import com.android.systemui.activityIntentHelper
+import com.android.systemui.bluetooth.mockBroadcastDialogController
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor
import com.android.systemui.media.controls.util.mediaInstanceId
+import com.android.systemui.media.mediaOutputDialogManager
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.statusbar.notificationLockscreenUserManager
+import com.android.systemui.statusbar.policy.keyguardStateController
val Kosmos.mediaControlInteractor by
Kosmos.Fixture {
MediaControlInteractor(
+ applicationContext = applicationContext,
instanceId = mediaInstanceId,
repository = mediaFilterRepository,
mediaDataProcessor = mediaDataProcessor,
+ keyguardStateController = keyguardStateController,
+ activityStarter = activityStarter,
+ activityIntentHelper = activityIntentHelper,
+ lockscreenUserManager = notificationLockscreenUserManager,
+ mediaOutputDialogManager = mediaOutputDialogManager,
+ broadcastDialogController = mockBroadcastDialogController,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt
new file mode 100644
index 0000000..2f3d3c3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.ui.viewmodel
+
+import android.content.applicationContext
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.media.controls.domain.pipeline.interactor.mediaControlInteractor
+import com.android.systemui.media.controls.util.mediaUiEventLogger
+
+val Kosmos.mediaControlViewModel by
+ Kosmos.Fixture {
+ MediaControlViewModel(
+ applicationContext = applicationContext,
+ backgroundDispatcher = testDispatcher,
+ backgroundExecutor = fakeExecutor,
+ interactor = mediaControlInteractor,
+ logger = mediaUiEventLogger,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/MediaFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/MediaFlagsKosmos.kt
index 6f652f2..e88d22a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/MediaFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/MediaFlagsKosmos.kt
@@ -18,9 +18,5 @@
import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
-val Kosmos.mediaFlags by
- Kosmos.Fixture {
- MediaFlags(featureFlags = featureFlagsClassic, sceneContainerFlags = sceneContainerFlags)
- }
+val Kosmos.mediaFlags by Kosmos.Fixture { MediaFlags(featureFlags = featureFlagsClassic) }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/reducebrightness/ReduceBrightColorsTileKosmos.kt
similarity index 63%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/reducebrightness/ReduceBrightColorsTileKosmos.kt
index f6140f5..339f3bb 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/reducebrightness/ReduceBrightColorsTileKosmos.kt
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.qs.tiles.impl.reducebrightness
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import com.android.systemui.accessibility.qs.QSAccessibilityModule
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+
+val Kosmos.qsReduceBrightColorsTileConfig by
+ Kosmos.Fixture { QSAccessibilityModule.provideReduceBrightColorsTileConfig(qsEventLogger) }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/screenrecord/ScreenRecordTileKosmos.kt
similarity index 64%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/screenrecord/ScreenRecordTileKosmos.kt
index f6140f5..ddcca94 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/FlowType.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/screenrecord/ScreenRecordTileKosmos.kt
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.qs.tiles.impl.screenrecord
-enum class FlowType {
- GET,
- CREATE
-}
\ No newline at end of file
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+import com.android.systemui.screenrecord.ScreenRecordModule
+
+val Kosmos.qsScreenRecordTileConfig by
+ Kosmos.Fixture { ScreenRecordModule.provideScreenRecordTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt
deleted file mode 100644
index bae5257..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.scene.shared.flag
-
-import dagger.Binds
-import dagger.Module
-import dagger.Provides
-
-class FakeSceneContainerFlags(
- var enabled: Boolean = false,
-) : SceneContainerFlags {
-
- override fun isEnabled(): Boolean {
- return enabled
- }
-
- override fun requirementDescription(): String {
- return ""
- }
-}
-
-@Module(includes = [FakeSceneContainerFlagsModule.Bindings::class])
-class FakeSceneContainerFlagsModule(
- @get:Provides val sceneContainerFlags: FakeSceneContainerFlags = FakeSceneContainerFlags(),
-) {
- @Module
- interface Bindings {
- @Binds fun bindFake(fake: FakeSceneContainerFlags): SceneContainerFlags
- }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
index 07e2d6b..543d5b6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
@@ -23,7 +23,6 @@
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.shade.ShadeModule
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
@@ -36,7 +35,6 @@
var Kosmos.baseShadeInteractor: BaseShadeInteractor by
Kosmos.Fixture {
ShadeModule.provideBaseShadeInteractor(
- sceneContainerFlags = sceneContainerFlags,
sceneContainerOn = { shadeInteractorSceneContainerImpl },
sceneContainerOff = { shadeInteractorLegacyImpl },
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
new file mode 100644
index 0000000..8d653f7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.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.systemui.shade.ui.viewmodel
+
+import android.content.applicationContext
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
+import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.mobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.mobileIconsViewModel
+
+val Kosmos.shadeHeaderViewModel: ShadeHeaderViewModel by
+ Kosmos.Fixture {
+ ShadeHeaderViewModel(
+ applicationScope = applicationCoroutineScope,
+ context = applicationContext,
+ activityStarter = activityStarter,
+ shadeInteractor = shadeInteractor,
+ mobileIconsInteractor = mobileIconsInteractor,
+ mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = privacyChipInteractor,
+ clockInteractor = shadeHeaderClockInteractor,
+ broadcastDispatcher = broadcastDispatcher,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
index 165c942..7bf77e5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
@@ -26,8 +26,11 @@
val Kosmos.headsUpNotificationRepository by Fixture { FakeHeadsUpNotificationRepository() }
class FakeHeadsUpNotificationRepository : HeadsUpRepository {
- override val headsUpAnimatingAway: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ override val isHeadsUpAnimatingAway: MutableStateFlow<Boolean> = MutableStateFlow(false)
override val topHeadsUpRow: Flow<HeadsUpRowRepository?> = MutableStateFlow(null)
override val activeHeadsUpRows: MutableStateFlow<Set<HeadsUpRowRepository>> =
MutableStateFlow(emptySet())
+ override fun setHeadsUpAnimatingAway(animatingAway: Boolean) {
+ isHeadsUpAnimatingAway.value = animatingAway
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
index c65d0a3..94f6ecd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.dump.dumpManager
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -48,5 +49,6 @@
userSetupInteractor,
zenModeInteractor,
testDispatcher,
+ dumpManager,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
index b249211..f0eea38 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
@@ -21,7 +21,6 @@
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
@@ -30,7 +29,6 @@
dumpManager = dumpManager,
interactor = notificationStackAppearanceInteractor,
shadeInteractor = shadeInteractor,
- flags = sceneContainerFlags,
featureFlags = featureFlagsClassic,
keyguardInteractor = keyguardInteractor,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index d2de835..cbba80b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -36,6 +36,7 @@
import com.android.systemui.keyguard.ui.viewmodel.lockscreenToOccludedTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.lockscreenToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.occludedToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.occludedToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.occludedToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.primaryBouncerToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.primaryBouncerToLockscreenTransitionViewModel
@@ -45,6 +46,7 @@
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
+import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
@OptIn(ExperimentalCoroutinesApi::class)
@@ -74,10 +76,12 @@
lockscreenToPrimaryBouncerTransitionViewModel =
lockscreenToPrimaryBouncerTransitionViewModel,
occludedToAodTransitionViewModel = occludedToAodTransitionViewModel,
+ occludedToGoneTransitionViewModel = occludedToGoneTransitionViewModel,
occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
primaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel,
primaryBouncerToLockscreenTransitionViewModel =
primaryBouncerToLockscreenTransitionViewModel,
aodBurnInViewModel = aodBurnInViewModel,
+ unfoldTransitionInteractor = unfoldTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt
index cf800d0..9c3f510 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt
@@ -24,7 +24,6 @@
import com.android.systemui.scene.data.repository.windowRootViewVisibilityRepository
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.policy.headsUpManager
@@ -36,7 +35,7 @@
headsUpManager = headsUpManager,
powerInteractor = powerInteractor,
activeNotificationsInteractor = activeNotificationsInteractor,
- sceneInteractorProvider = { sceneInteractor },
- sceneContainerFlags = sceneContainerFlags,
- )
+ ) {
+ sceneInteractor
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
similarity index 94%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
index 638925d..74b2da4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
new file mode 100644
index 0000000..386c7c5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.airplane.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+
+val Kosmos.airplaneModeInteractor: AirplaneModeInteractor by
+ Kosmos.Fixture {
+ AirplaneModeInteractor(
+ FakeAirplaneModeRepository(),
+ FakeConnectivityRepository(),
+ FakeMobileConnectionsRepository(),
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index 2d5a361..eb2d6c0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -31,6 +31,7 @@
override val tableLogBuffer: TableLogBuffer,
) : MobileConnectionRepository {
override val carrierId = MutableStateFlow(UNKNOWN_CARRIER_ID)
+ override val inflateSignalStrength: MutableStateFlow<Boolean> = MutableStateFlow(false)
override val isEmergencyOnly = MutableStateFlow(false)
override val isRoaming = MutableStateFlow(false)
override val operatorAlphaShort: MutableStateFlow<String?> = MutableStateFlow(null)
@@ -63,8 +64,6 @@
override val hasPrioritizedNetworkCapabilities = MutableStateFlow(false)
- override val satelliteConnectionHysteresisSeconds = MutableStateFlow(0)
-
private var isInEcmMode: Boolean = false
override suspend fun isInEcmMode(): Boolean = isInEcmMode
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKosmos.kt
new file mode 100644
index 0000000..eb6265a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKosmos.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.mobileIconsInteractor: MobileIconsInteractor by
+ Kosmos.Fixture { fakeMobileIconsInteractor }
+val Kosmos.fakeMobileIconsInteractor: FakeMobileIconsInteractor by
+ Kosmos.Fixture { FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock()) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKosmos.kt
new file mode 100644
index 0000000..c5f6557
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKosmos.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
+
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.mobileIconsInteractor
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.mobileIconsViewModel: MobileIconsViewModel by
+ Kosmos.Fixture {
+ MobileIconsViewModel(
+ logger = mock(),
+ verboseLogger = mock(),
+ interactor = mobileIconsInteractor,
+ airplaneModeInteractor = airplaneModeInteractor,
+ constants = mock(),
+ flags = featureFlagsClassic,
+ scope = applicationCoroutineScope,
+ )
+ }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
similarity index 97%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
index 28d632d..331e2fa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/truth/TruthUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/truth/TruthUtils.kt
new file mode 100644
index 0000000..64fed68
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/truth/TruthUtils.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.truth
+
+import com.google.common.truth.MapSubject
+import com.google.common.truth.Ordered
+
+fun MapSubject.containsEntriesExactly(entry: Pair<*, *>, vararg entries: Pair<*, *>): Ordered =
+ containsExactly(
+ entry.first,
+ entry.second,
+ *entries
+ .asSequence()
+ .flatMap { (key, value) -> sequenceOf(key, value) }
+ .toList()
+ .toTypedArray()
+ )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/FakeUnfoldTransitionProvider.kt
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/unfold/FakeUnfoldTransitionProvider.kt
index 56c6245..94f0c44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/FakeUnfoldTransitionProvider.kt
@@ -2,7 +2,7 @@
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-class TestUnfoldTransitionProvider : UnfoldTransitionProgressProvider, TransitionProgressListener {
+class FakeUnfoldTransitionProvider : UnfoldTransitionProgressProvider, TransitionProgressListener {
private val listeners = mutableListOf<TransitionProgressListener>()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/UnfoldTransitionProgressProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/UnfoldTransitionProgressProviderKosmos.kt
index 7c54a57..a0f5b58 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/UnfoldTransitionProgressProviderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/UnfoldTransitionProgressProviderKosmos.kt
@@ -18,6 +18,8 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
-var Kosmos.unfoldTransitionProgressProvider by Fixture { mock<UnfoldTransitionProgressProvider>() }
+val Kosmos.fakeUnfoldTransitionProgressProvider by Fixture { FakeUnfoldTransitionProvider() }
+
+val Kosmos.unfoldTransitionProgressProvider by
+ Fixture<UnfoldTransitionProgressProvider> { fakeUnfoldTransitionProgressProvider }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorKosmos.kt
index d03616a..6faa3b4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorKosmos.kt
@@ -16,10 +16,14 @@
package com.android.systemui.unfold.domain.interactor
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.unfold.data.repository.unfoldTransitionRepository
val Kosmos.unfoldTransitionInteractor by Fixture {
- UnfoldTransitionInteractorImpl(repository = unfoldTransitionRepository)
+ UnfoldTransitionInteractor(
+ repository = unfoldTransitionRepository,
+ configurationInteractor = configurationInteractor,
+ )
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt
index d341073..348a02e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt
@@ -24,6 +24,7 @@
import com.android.systemui.volume.panel.dagger.factory.KosmosVolumePanelComponentFactory
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
import com.android.systemui.volume.panel.domain.TestComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractorImpl
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
@@ -44,6 +45,8 @@
var Kosmos.componentsLayoutManager: ComponentsLayoutManager by Kosmos.Fixture()
var Kosmos.enabledComponents: Collection<VolumePanelComponentKey> by
Kosmos.Fixture { componentByKey.keys }
+var Kosmos.volumePanelStartables: Set<VolumePanelStartable> by
+ Kosmos.Fixture { emptySet<VolumePanelStartable>() }
val Kosmos.unavailableCriteria: Provider<ComponentAvailabilityCriteria> by
Kosmos.Fixture { Provider { TestComponentAvailabilityCriteria(false) } }
val Kosmos.availableCriteria: Provider<ComponentAvailabilityCriteria> by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
index b66d7f9..d4a72b4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
@@ -24,8 +24,9 @@
private val sliceByWidth = mutableMapOf<Int, MutableStateFlow<Slice?>>()
- override fun ancSlice(width: Int): Flow<Slice?> =
- sliceByWidth.getOrPut(width) { MutableStateFlow(null) }
+ override fun ancSlice(width: Int, isCollapsed: Boolean, hideLabel: Boolean): Flow<Slice?> {
+ return sliceByWidth.getOrPut(width) { MutableStateFlow(null) }
+ }
fun putSlice(width: Int, slice: Slice?) {
sliceByWidth.getOrPut(width) { MutableStateFlow(null) }.value = slice
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/dagger/factory/KosmosVolumePanelComponentFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/dagger/factory/KosmosVolumePanelComponentFactory.kt
index 49041ed..e5f5d4e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/dagger/factory/KosmosVolumePanelComponentFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/dagger/factory/KosmosVolumePanelComponentFactory.kt
@@ -22,10 +22,12 @@
import com.android.systemui.volume.panel.componentsInteractor
import com.android.systemui.volume.panel.componentsLayoutManager
import com.android.systemui.volume.panel.dagger.VolumePanelComponent
+import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
import com.android.systemui.volume.panel.ui.composable.ComponentsFactory
import com.android.systemui.volume.panel.ui.layout.ComponentsLayoutManager
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
+import com.android.systemui.volume.panel.volumePanelStartables
import kotlinx.coroutines.CoroutineScope
class KosmosVolumePanelComponentFactory(private val kosmos: Kosmos) : VolumePanelComponentFactory {
@@ -41,5 +43,8 @@
override fun componentsLayoutManager(): ComponentsLayoutManager =
kosmos.componentsLayoutManager
+
+ override fun volumePanelStartables(): Set<VolumePanelStartable> =
+ kosmos.volumePanelStartables
}
}
diff --git a/packages/SystemUI/utils/Android.bp b/packages/SystemUI/utils/Android.bp
new file mode 100644
index 0000000..1ef3816
--- /dev/null
+++ b/packages/SystemUI/utils/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+java_library {
+ name: "SystemUI-shared-utils",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "kotlinx_coroutines",
+ ],
+}
diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/FlowConflated.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/FlowConflated.kt
new file mode 100644
index 0000000..ed97c60
--- /dev/null
+++ b/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/FlowConflated.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalTypeInference::class)
+
+package com.android.systemui.utils.coroutines.flow
+
+import kotlin.experimental.ExperimentalTypeInference
+import kotlinx.coroutines.channels.ProducerScope
+import kotlinx.coroutines.channels.SendChannel
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.channelFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.produceIn
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+/**
+ * Creates an instance of a _cold_ [Flow] with elements that are sent to a [SendChannel] provided to
+ * the builder's [block] of code via [ProducerScope]. It allows elements to be produced by code that
+ * is running in a different context or concurrently.
+ *
+ * The resulting flow is _cold_, which means that [block] is called every time a terminal operator
+ * is applied to the resulting flow.
+ *
+ * This builder ensures thread-safety and context preservation, thus the provided [ProducerScope]
+ * can be used from any context, e.g. from a callback-based API. The resulting flow completes as
+ * soon as the code in the [block] completes. [awaitClose] should be used to keep the flow running,
+ * otherwise the channel will be closed immediately when block completes. [awaitClose] argument is
+ * called either when a flow consumer cancels the flow collection or when a callback-based API
+ * invokes [SendChannel.close] manually and is typically used to cleanup the resources after the
+ * completion, e.g. unregister a callback. Using [awaitClose] is mandatory in order to prevent
+ * memory leaks when the flow collection is cancelled, otherwise the callback may keep running even
+ * when the flow collector is already completed. To avoid such leaks, this method throws
+ * [IllegalStateException] if block returns, but the channel is not closed yet.
+ *
+ * A [conflated][conflate] channel is used. Use the [buffer] operator on the resulting flow to
+ * specify a user-defined value and to control what happens when data is produced faster than
+ * consumed, i.e. to control the back-pressure behavior.
+ *
+ * Adjacent applications of [callbackFlow], [flowOn], [buffer], and [produceIn] are always fused so
+ * that only one properly configured channel is used for execution.
+ *
+ * Example of usage that converts a multi-shot callback API to a flow. For single-shot callbacks use
+ * [suspendCancellableCoroutine].
+ *
+ * ```
+ * fun flowFrom(api: CallbackBasedApi): Flow<T> = callbackFlow {
+ * val callback = object : Callback { // Implementation of some callback interface
+ * override fun onNextValue(value: T) {
+ * // To avoid blocking you can configure channel capacity using
+ * // either buffer(Channel.CONFLATED) or buffer(Channel.UNLIMITED) to avoid overfill
+ * trySendBlocking(value)
+ * .onFailure { throwable ->
+ * // Downstream has been cancelled or failed, can log here
+ * }
+ * }
+ * override fun onApiError(cause: Throwable) {
+ * cancel(CancellationException("API Error", cause))
+ * }
+ * override fun onCompleted() = channel.close()
+ * }
+ * api.register(callback)
+ * /*
+ * * Suspends until either 'onCompleted'/'onApiError' from the callback is invoked
+ * * or flow collector is cancelled (e.g. by 'take(1)' or because a collector's coroutine was cancelled).
+ * * In both cases, callback will be properly unregistered.
+ * */
+ * awaitClose { api.unregister(callback) }
+ * }
+ * ```
+ * > The callback `register`/`unregister` methods provided by an external API must be thread-safe,
+ * > because `awaitClose` block can be called at any time due to asynchronous nature of
+ * > cancellation, even concurrently with the call of the callback.
+ *
+ * This builder is to be preferred over [callbackFlow], due to the latter's default configuration of
+ * using an internal buffer, negatively impacting system health.
+ *
+ * @see callbackFlow
+ */
+fun <T> conflatedCallbackFlow(
+ @BuilderInference block: suspend ProducerScope<T>.() -> Unit,
+): Flow<T> = callbackFlow(block).conflate()
+
+/**
+ * Creates an instance of a _cold_ [Flow] with elements that are sent to a [SendChannel] provided to
+ * the builder's [block] of code via [ProducerScope]. It allows elements to be produced by code that
+ * is running in a different context or concurrently. The resulting flow is _cold_, which means that
+ * [block] is called every time a terminal operator is applied to the resulting flow.
+ *
+ * This builder ensures thread-safety and context preservation, thus the provided [ProducerScope]
+ * can be used concurrently from different contexts. The resulting flow completes as soon as the
+ * code in the [block] and all its children completes. Use [awaitClose] as the last statement to
+ * keep it running. A more detailed example is provided in the documentation of [callbackFlow].
+ *
+ * A [conflated][conflate] channel is used. Use the [buffer] operator on the resulting flow to
+ * specify a user-defined value and to control what happens when data is produced faster than
+ * consumed, i.e. to control the back-pressure behavior.
+ *
+ * Adjacent applications of [channelFlow], [flowOn], [buffer], and [produceIn] are always fused so
+ * that only one properly configured channel is used for execution.
+ *
+ * Examples of usage:
+ * ```
+ * fun <T> Flow<T>.merge(other: Flow<T>): Flow<T> = channelFlow {
+ * // collect from one coroutine and send it
+ * launch {
+ * collect { send(it) }
+ * }
+ * // collect and send from this coroutine, too, concurrently
+ * other.collect { send(it) }
+ * }
+ *
+ * fun <T> contextualFlow(): Flow<T> = channelFlow {
+ * // send from one coroutine
+ * launch(Dispatchers.IO) {
+ * send(computeIoValue())
+ * }
+ * // send from another coroutine, concurrently
+ * launch(Dispatchers.Default) {
+ * send(computeCpuValue())
+ * }
+ * }
+ * ```
+ *
+ * This builder is to be preferred over [channelFlow], due to the latter's default configuration of
+ * using an internal buffer, negatively impacting system health.
+ *
+ * @see channelFlow
+ */
+fun <T> conflatedChannelFlow(
+ @BuilderInference block: suspend ProducerScope<T>.() -> Unit,
+): Flow<T> = channelFlow(block).conflate()
diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/LatestConflated.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/LatestConflated.kt
new file mode 100644
index 0000000..5f8c660
--- /dev/null
+++ b/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/LatestConflated.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalTypeInference::class)
+
+package com.android.systemui.utils.coroutines.flow
+
+import kotlin.experimental.ExperimentalTypeInference
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.FlowCollector
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.transformLatest
+
+/**
+ * Returns a flow that emits elements from the original flow transformed by [transform] function.
+ * When the original flow emits a new value, computation of the [transform] block for previous value
+ * is cancelled.
+ *
+ * For example, the following flow:
+ * ```
+ * flow {
+ * emit("a")
+ * delay(100)
+ * emit("b")
+ * }.mapLatest { value ->
+ * println("Started computing $value")
+ * delay(200)
+ * "Computed $value"
+ * }
+ * ```
+ *
+ * will print "Started computing a" and "Started computing b", but the resulting flow will contain
+ * only "Computed b" value.
+ *
+ * This operator is [conflated][conflate] by default, and as such should be preferred over usage of
+ * [mapLatest], due to the latter's default configuration of using an internal buffer, negatively
+ * impacting system health.
+ *
+ * @see mapLatest
+ */
+fun <T, R> Flow<T>.mapLatestConflated(@BuilderInference transform: suspend (T) -> R): Flow<R> =
+ mapLatest(transform).conflate()
+
+/**
+ * Returns a flow that switches to a new flow produced by [transform] function every time the
+ * original flow emits a value. When the original flow emits a new value, the previous flow produced
+ * by `transform` block is cancelled.
+ *
+ * For example, the following flow:
+ * ```
+ * flow {
+ * emit("a")
+ * delay(100)
+ * emit("b")
+ * }.flatMapLatest { value ->
+ * flow {
+ * emit(value)
+ * delay(200)
+ * emit(value + "_last")
+ * }
+ * }
+ * ```
+ *
+ * produces `a b b_last`
+ *
+ * This operator is [conflated][conflate] by default, and as such should be preferred over usage of
+ * [flatMapLatest], due to the latter's default configuration of using an internal buffer,
+ * negatively impacting system health.
+ *
+ * @see flatMapLatest
+ */
+fun <T, R> Flow<T>.flatMapLatestConflated(
+ @BuilderInference transform: suspend (T) -> Flow<R>,
+): Flow<R> = flatMapLatest(transform).conflate()
+
+/**
+ * Returns a flow that produces element by [transform] function every time the original flow emits a
+ * value. When the original flow emits a new value, the previous `transform` block is cancelled,
+ * thus the name `transformLatest`.
+ *
+ * For example, the following flow:
+ * ```
+ * flow {
+ * emit("a")
+ * delay(100)
+ * emit("b")
+ * }.transformLatest { value ->
+ * emit(value)
+ * delay(200)
+ * emit(value + "_last")
+ * }
+ * ```
+ *
+ * produces `a b b_last`.
+ *
+ * This operator is [conflated][conflate] by default, and as such should be preferred over usage of
+ * [transformLatest], due to the latter's default configuration of using an internal buffer,
+ * negatively impacting system health.
+ *
+ * @see transformLatest
+ */
+fun <T, R> Flow<T>.transformLatestConflated(
+ @BuilderInference transform: suspend FlowCollector<R>.(T) -> Unit,
+): Flow<R> = transformLatest(transform).conflate()
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 23e269a..cbbce1a 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -19,6 +19,7 @@
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_INELIGIBLE;
import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_METADATA;
@@ -39,6 +40,7 @@
import android.content.SharedPreferences;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
@@ -109,22 +111,16 @@
static final String LOCK_WALLPAPER_STAGE = "wallpaper-lock-stage";
@VisibleForTesting
static final String WALLPAPER_INFO_STAGE = "wallpaper-info-stage";
-
@VisibleForTesting
static final String WALLPAPER_BACKUP_DEVICE_INFO_STAGE = "wallpaper-backup-device-info-stage";
-
static final String EMPTY_SENTINEL = "empty";
static final String QUOTA_SENTINEL = "quota";
-
// Shared preferences constants.
static final String PREFS_NAME = "wbprefs.xml";
static final String SYSTEM_GENERATION = "system_gen";
static final String LOCK_GENERATION = "lock_gen";
- /**
- * An approximate area threshold to compare device dimension similarity
- */
- static final int AREA_THRESHOLD = 50; // TODO (b/327637867): determine appropriate threshold
+ static final float DEFAULT_ACCEPTABLE_PARALLAX = 0.2f;
// If this file exists, it means we exceeded our quota last time
private File mQuotaFile;
@@ -336,7 +332,6 @@
mEventLogger.onSystemImageWallpaperBackupFailed(error);
}
-
private void backupLockWallpaperFileIfItExists(SharedPreferences sharedPrefs,
boolean lockChanged, int lockGeneration, FullBackupDataOutput data) throws IOException {
final File lockImageStage = new File(getFilesDir(), LOCK_WALLPAPER_STAGE);
@@ -409,6 +404,16 @@
}
}
+ private static String readText(TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String result = "";
+ if (parser.next() == XmlPullParser.TEXT) {
+ result = parser.getText();
+ parser.nextTag();
+ }
+ return result;
+ }
+
@VisibleForTesting
// fullBackupFile is final, so we intercept backups here in tests.
protected void backupFile(File file, FullBackupDataOutput data) {
@@ -438,18 +443,10 @@
boolean lockImageStageExists = lockImageStage.exists();
try {
- // Parse the device dimensions of the source device and compare with target to
- // to identify whether we need to skip the remainder of the restore process
+ // Parse the device dimensions of the source device
Pair<Point, Point> sourceDeviceDimensions = parseDeviceDimensions(
deviceDimensionsStage);
- Point targetDeviceDimensions = getScreenDimensions();
- if (sourceDeviceDimensions != null && targetDeviceDimensions != null
- && isSourceDeviceSignificantlySmallerThanTarget(sourceDeviceDimensions.first,
- targetDeviceDimensions)) {
- Slog.d(TAG, "The source device is significantly smaller than target");
- }
-
// First parse the live component name so that we know for logging if we care about
// logging errors with the image restore.
ComponentName wpService = parseWallpaperComponent(infoStage, "wp");
@@ -466,9 +463,10 @@
// to back up the original image on the source device, or there was no user-supplied
// wallpaper image present.
if (lockImageStageExists) {
- restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK);
+ restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK,
+ sourceDeviceDimensions);
}
- restoreFromStage(imageStage, infoStage, "wp", sysWhich);
+ restoreFromStage(imageStage, infoStage, "wp", sysWhich, sourceDeviceDimensions);
// And reset to the wallpaper service we should be using
if (mLockHasLiveComponent) {
@@ -543,16 +541,6 @@
}
}
- private static String readText(TypedXmlPullParser parser)
- throws IOException, XmlPullParserException {
- String result = "";
- if (parser.next() == XmlPullParser.TEXT) {
- result = parser.getText();
- parser.nextTag();
- }
- return result;
- }
-
@VisibleForTesting
void updateWallpaperComponent(ComponentName wpService, int which)
throws IOException {
@@ -578,10 +566,13 @@
}
}
- private void restoreFromStage(File stage, File info, String hintTag, int which)
+ private void restoreFromStage(File stage, File info, String hintTag, int which,
+ Pair<Point, Point> sourceDeviceDimensions)
throws IOException {
if (stage.exists()) {
if (multiCrop()) {
+ // TODO(b/332937943): implement offset adjustment by manually adjusting crop to
+ // adhere to device aspect ratio
SparseArray<Rect> cropHints = parseCropHints(info, hintTag);
if (cropHints != null) {
Slog.i(TAG, "Got restored wallpaper; applying which=" + which
@@ -601,7 +592,6 @@
}
return;
}
-
// Parse the restored info file to find the crop hint. Note that this currently
// relies on a priori knowledge of the wallpaper info file schema.
Rect cropHint = parseCropHint(info, hintTag);
@@ -609,8 +599,33 @@
Slog.i(TAG, "Got restored wallpaper; applying which=" + which
+ "; cropHint = " + cropHint);
try (FileInputStream in = new FileInputStream(stage)) {
- mWallpaperManager.setStream(in, cropHint.isEmpty() ? null : cropHint, true,
- which);
+
+ if (sourceDeviceDimensions != null && sourceDeviceDimensions.first != null) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ ParcelFileDescriptor pdf = ParcelFileDescriptor.open(stage, MODE_READ_ONLY);
+ BitmapFactory.decodeFileDescriptor(pdf.getFileDescriptor(),
+ null, options);
+ Point bitmapSize = new Point(options.outWidth, options.outHeight);
+ Point sourceDeviceSize = new Point(sourceDeviceDimensions.first.x,
+ sourceDeviceDimensions.first.y);
+ Point targetDeviceDimensions = getScreenDimensions();
+
+ // TODO: for now we handle only the case where the target device has smaller
+ // aspect ratio than the source device i.e. the target device is more narrow
+ // than the source device
+ if (isTargetMoreNarrowThanSource(targetDeviceDimensions,
+ sourceDeviceSize)) {
+ Rect adjustedCrop = findNewCropfromOldCrop(cropHint,
+ sourceDeviceDimensions.first, true, targetDeviceDimensions,
+ bitmapSize, true);
+
+ cropHint.set(adjustedCrop);
+ }
+ }
+
+ mWallpaperManager.setStream(in, cropHint.isEmpty() ? null : cropHint,
+ true, which);
// And log the success
if ((which & FLAG_SYSTEM) > 0) {
@@ -629,6 +644,209 @@
}
}
+ /**
+ * This method computes the crop of the stored wallpaper to preserve its center point as the
+ * user had set it in the previous device.
+ *
+ * The algorithm involves first computing the original crop of the user (without parallax). Then
+ * manually adjusting the user's original crop to respect the current device's aspect ratio
+ * (thereby preserving the center point). Then finally, adding any leftover image real-estate
+ * (i.e. space left over on the horizontal axis) to add parallax effect. Parallax is only added
+ * if was present in the old device's settings.
+ *
+ */
+ private Rect findNewCropfromOldCrop(Rect oldCrop, Point oldDisplaySize, boolean oldRtl,
+ Point newDisplaySize, Point bitmapSize, boolean newRtl) {
+ Rect cropWithoutParallax = withoutParallax(oldCrop, oldDisplaySize, oldRtl, bitmapSize);
+ oldCrop = oldCrop.isEmpty() ? new Rect(0, 0, bitmapSize.x, bitmapSize.y) : oldCrop;
+ float oldParallaxAmount = ((float) oldCrop.width() / cropWithoutParallax.width()) - 1;
+
+ Rect newCropWithSameCenterWithoutParallax = sameCenter(newDisplaySize, bitmapSize,
+ cropWithoutParallax);
+
+ Rect newCrop = newCropWithSameCenterWithoutParallax;
+
+ // calculate the amount of left-over space there is in the image after adjusting the crop
+ // from the above operation i.e. in a rtl configuration, this is the remaining space in the
+ // image after subtracting the new crop's right edge coordinate from the image itself, and
+ // for ltr, its just the new crop's left edge coordinate (as it's the distance from the
+ // beginning of the image)
+ int widthAvailableForParallaxOnTheNewDevice =
+ (newRtl) ? newCrop.left : bitmapSize.x - newCrop.right;
+
+ // calculate relatively how much this available space is as a fraction of the total cropped
+ // image
+ float availableParallaxAmount =
+ (float) widthAvailableForParallaxOnTheNewDevice / newCrop.width();
+
+ float minAcceptableParallax = Math.min(DEFAULT_ACCEPTABLE_PARALLAX, oldParallaxAmount);
+
+ if (DEBUG) {
+ Slog.d(TAG, "- cropWithoutParallax: " + cropWithoutParallax);
+ Slog.d(TAG, "- oldParallaxAmount: " + oldParallaxAmount);
+ Slog.d(TAG, "- newCropWithSameCenterWithoutParallax: "
+ + newCropWithSameCenterWithoutParallax);
+ Slog.d(TAG, "- widthAvailableForParallaxOnTheNewDevice: "
+ + widthAvailableForParallaxOnTheNewDevice);
+ Slog.d(TAG, "- availableParallaxAmount: " + availableParallaxAmount);
+ Slog.d(TAG, "- minAcceptableParallax: " + minAcceptableParallax);
+ Slog.d(TAG, "- oldCrop: " + oldCrop);
+ Slog.d(TAG, "- oldDisplaySize: " + oldDisplaySize);
+ Slog.d(TAG, "- oldRtl: " + oldRtl);
+ Slog.d(TAG, "- newDisplaySize: " + newDisplaySize);
+ Slog.d(TAG, "- bitmapSize: " + bitmapSize);
+ Slog.d(TAG, "- newRtl: " + newRtl);
+ }
+ if (availableParallaxAmount >= minAcceptableParallax) {
+ // but in any case, don't put more parallax than the amount of the old device
+ float parallaxToAdd = Math.min(availableParallaxAmount, oldParallaxAmount);
+
+ int widthToAddForParallax = (int) (newCrop.width() * parallaxToAdd);
+ if (DEBUG) {
+ Slog.d(TAG, "- parallaxToAdd: " + parallaxToAdd);
+ Slog.d(TAG, "- widthToAddForParallax: " + widthToAddForParallax);
+ }
+ if (newRtl) {
+ newCrop.left -= widthToAddForParallax;
+ } else {
+ newCrop.right += widthToAddForParallax;
+ }
+ }
+ return newCrop;
+ }
+
+ /**
+ * This method computes the original crop of the user without parallax.
+ *
+ * NOTE: When the user sets the wallpaper with a specific crop, there may additional image added
+ * to the crop to support parallax. In order to determine the user's actual crop the parallax
+ * must be removed if it exists.
+ */
+ Rect withoutParallax(Rect crop, Point displaySize, boolean rtl, Point bitmapSize) {
+ // in the case an image's crop is not set, we assume the image itself is cropped
+ if (crop.isEmpty()) {
+ crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
+ }
+
+ if (DEBUG) {
+ Slog.w(TAG, "- crop: " + crop);
+ }
+
+ Rect adjustedCrop = new Rect(crop);
+ float suggestedDisplayRatio = (float) displaySize.x / displaySize.y;
+
+ // here we calculate the width of the wallpaper image such that it has the same aspect ratio
+ // as the given display i.e. the width of the image on a single page of the device without
+ // parallax (i.e. displaySize will correspond to the display the crop was originally set on)
+ int wallpaperWidthWithoutParallax = (int) (0.5f + (float) displaySize.x * crop.height()
+ / displaySize.y);
+ // subtracting wallpaperWidthWithoutParallax from the wallpaper crop gives the amount of
+ // parallax added
+ int widthToRemove = Math.max(0, crop.width() - wallpaperWidthWithoutParallax);
+
+ if (DEBUG) {
+ Slog.d(TAG, "- adjustedCrop: " + adjustedCrop);
+ Slog.d(TAG, "- suggestedDisplayRatio: " + suggestedDisplayRatio);
+ Slog.d(TAG, "- wallpaperWidthWithoutParallax: " + wallpaperWidthWithoutParallax);
+ Slog.d(TAG, "- widthToRemove: " + widthToRemove);
+ }
+ if (rtl) {
+ adjustedCrop.left += widthToRemove;
+ } else {
+ adjustedCrop.right -= widthToRemove;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "- adjustedCrop: " + crop);
+ }
+ return adjustedCrop;
+ }
+
+ /**
+ * This method computes a new crop based on the given crop in order to preserve the center point
+ * of the given crop on the provided displaySize. This is only for the case where the device
+ * displaySize has a smaller aspect ratio than the cropped image.
+ *
+ * NOTE: If the width to height ratio is less in the device display than cropped image
+ * this means the aspect ratios are off and there will be distortions in the image
+ * if the image is applied to the current display (i.e. the image will be skewed ->
+ * pixels in the image will not align correctly with the same pixels in the image that are
+ * above them)
+ */
+ Rect sameCenter(Point displaySize, Point bitmapSize, Rect crop) {
+
+ // in the case an image's crop is not set, we assume the image itself is cropped
+ if (crop.isEmpty()) {
+ crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
+ }
+
+ float screenRatio = (float) displaySize.x / displaySize.y;
+ float cropRatio = (float) crop.width() / crop.height();
+
+ Rect adjustedCrop = new Rect(crop);
+
+ if (screenRatio < cropRatio) {
+ // the screen is more narrow than the image, and as such, the image will need to be
+ // zoomed in till it fits in the vertical axis. Due to this, we need to manually adjust
+ // the image's crop in order for it to fit into the screen without having the framework
+ // do it (since the framework left aligns the image after zooming)
+
+ // Calculate the height of the adjusted wallpaper crop so it respects the aspect ratio
+ // of the device. To calculate the height, we will use the width of the current crop.
+ // This is so we find the largest height possible which also respects the device aspect
+ // ratio.
+ int heightToAdd = (int) (0.5f + crop.width() / screenRatio - crop.height());
+
+ // Calculate how much extra image space available that can be used to adjust
+ // the crop. If this amount is less than heightToAdd, from above, then that means we
+ // can't use heightToAdd. Instead we will need to use the maximum possible height, which
+ // is the height of the original bitmap. NOTE: the bitmap height may be different than
+ // the crop.
+ // since there is no guarantee to have height available on both sides
+ // (e.g. the available height might be fully at the bottom), grab the minimum
+ int availableHeight = 2 * Math.min(crop.top, bitmapSize.y - crop.bottom);
+ int actualHeightToAdd = Math.min(heightToAdd, availableHeight);
+
+ // half of the additional height is added to the top and bottom of the crop
+ adjustedCrop.top -= actualHeightToAdd / 2 + actualHeightToAdd % 2;
+ adjustedCrop.bottom += actualHeightToAdd / 2;
+
+ // Calculate the width of the adjusted crop. Initially we used the fixed width of the
+ // crop to calculate the heightToAdd, but since this height may be invalid (based on
+ // the calculation above) we calculate the width again instead of using the fixed width,
+ // using the adjustedCrop's updated height.
+ int widthToRemove = (int) (0.5f + crop.width() - adjustedCrop.height() * screenRatio);
+
+ // half of the additional width is subtracted from the left and right side of the crop
+ int widthToRemoveLeft = widthToRemove / 2;
+ int widthToRemoveRight = widthToRemove / 2 + widthToRemove % 2;
+
+ adjustedCrop.left += widthToRemoveLeft;
+ adjustedCrop.right -= widthToRemoveRight;
+
+ if (DEBUG) {
+ Slog.d(TAG, "cropRatio: " + cropRatio);
+ Slog.d(TAG, "screenRatio: " + screenRatio);
+ Slog.d(TAG, "heightToAdd: " + heightToAdd);
+ Slog.d(TAG, "actualHeightToAdd: " + actualHeightToAdd);
+ Slog.d(TAG, "availableHeight: " + availableHeight);
+ Slog.d(TAG, "widthToRemove: " + widthToRemove);
+ Slog.d(TAG, "adjustedCrop: " + adjustedCrop);
+ }
+
+ return adjustedCrop;
+ }
+
+ return adjustedCrop;
+ }
+
+ private boolean isTargetMoreNarrowThanSource(Point targetDisplaySize, Point srcDisplaySize) {
+ float targetScreenRatio = (float) targetDisplaySize.x / targetDisplaySize.y;
+ float srcScreenRatio = (float) srcDisplaySize.x / srcDisplaySize.y;
+
+ return (targetScreenRatio < srcScreenRatio);
+ }
+
private void logRestoreErrorIfNoLiveComponent(int which, String error) {
if (mSystemHasLiveComponent) {
return;
@@ -644,6 +862,7 @@
mEventLogger.onLockImageWallpaperRestoreFailed(error);
}
}
+
private Rect parseCropHint(File wallpaperInfo, String sectionTag) {
Rect cropHint = new Rect();
try (FileInputStream stream = new FileInputStream(wallpaperInfo)) {
@@ -681,7 +900,7 @@
if (type != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
if (!sectionTag.equals(tag)) continue;
- for (Pair<Integer, String> pair: List.of(
+ for (Pair<Integer, String> pair : List.of(
new Pair<>(WallpaperManager.PORTRAIT, "Portrait"),
new Pair<>(WallpaperManager.LANDSCAPE, "Landscape"),
new Pair<>(WallpaperManager.SQUARE_PORTRAIT, "SquarePortrait"),
@@ -907,22 +1126,6 @@
return internalDisplays;
}
- /**
- * This method compares the source and target dimensions, and returns true if there is a
- * significant difference in area between them and the source dimensions are smaller than the
- * target dimensions.
- *
- * @param sourceDimensions is the dimensions of the source device
- * @param targetDimensions is the dimensions of the target device
- */
- @VisibleForTesting
- boolean isSourceDeviceSignificantlySmallerThanTarget(Point sourceDimensions,
- Point targetDimensions) {
- int rawAreaDelta = (targetDimensions.x * targetDimensions.y)
- - (sourceDimensions.x * sourceDimensions.y);
- return rawAreaDelta > AREA_THRESHOLD;
- }
-
@VisibleForTesting
boolean isDeviceInRestore() {
try {
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index ec9223c..3ecdf3f 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -59,7 +59,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
@@ -841,26 +840,6 @@
testParseCropHints(testMap);
}
- @Test
- public void test_sourceDimensionsAreLargerThanTarget() {
- // source device is larger than target, expecting to get false
- Point sourceDimensions = new Point(2208, 1840);
- Point targetDimensions = new Point(1080, 2092);
- boolean isSourceSmaller = mWallpaperBackupAgent
- .isSourceDeviceSignificantlySmallerThanTarget(sourceDimensions, targetDimensions);
- assertThat(isSourceSmaller).isEqualTo(false);
- }
-
- @Test
- public void test_sourceDimensionsMuchSmallerThanTarget() {
- // source device is smaller than target, expecting to get true
- Point sourceDimensions = new Point(1080, 2092);
- Point targetDimensions = new Point(2208, 1840);
- boolean isSourceSmaller = mWallpaperBackupAgent
- .isSourceDeviceSignificantlySmallerThanTarget(sourceDimensions, targetDimensions);
- assertThat(isSourceSmaller).isEqualTo(true);
- }
-
private void testParseCropHints(Map<Integer, Rect> testMap) throws Exception {
assumeTrue(multiCrop());
mockRestoredStaticWallpaperFile(testMap);
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml
index ff920b2..97559b4 100644
--- a/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"लुकाइयोस्"</string>
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"लुकाउनुहोस्"</string>
</resources>
diff --git a/proto/src/am_capabilities.proto b/proto/src/am_capabilities.proto
index d97bf81..fc9f7a45 100644
--- a/proto/src/am_capabilities.proto
+++ b/proto/src/am_capabilities.proto
@@ -7,6 +7,16 @@
string name = 1;
}
+message VMCapability {
+ string name = 1;
+}
+
+message FrameworkCapability {
+ string name = 1;
+}
+
message Capabilities {
repeated Capability values = 1;
+ repeated VMCapability vm_capabilities = 2;
+ repeated FrameworkCapability framework_capabilities = 3;
}
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java
index 7414110..0f65544 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java
@@ -100,6 +100,16 @@
mLastStateChangeTimestampMs = timestampMs;
}
+ public void copyStatesFrom(LongArrayMultiStateCounterRavenwood source) {
+ for (int i = 0; i < mStateCount; i++) {
+ mStates[i].mTimeInStateSinceUpdate = source.mStates[i].mTimeInStateSinceUpdate;
+ Arrays.fill(mStates[i].mCounter, 0);
+ }
+ mCurrentState = source.mCurrentState;
+ mLastStateChangeTimestampMs = source.mLastStateChangeTimestampMs;
+ mLastUpdateTimestampMs = source.mLastUpdateTimestampMs;
+ }
+
public void setValue(int state, long[] values) {
System.arraycopy(values, 0, mStates[state].mCounter, 0, mArrayLength);
}
@@ -335,6 +345,10 @@
getInstance(instanceId).setState(state, timestampMs);
}
+ public static void native_copyStatesFrom(long targetInstanceId, long sourceInstanceId) {
+ getInstance(targetInstanceId).copyStatesFrom(getInstance(sourceInstanceId));
+ }
+
public static void native_incrementValues(long instanceId, long containerInstanceId,
long timestampMs) {
getInstance(instanceId).incrementValues(
diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md
index 7c0cee8..2ab43bb 100644
--- a/ravenwood/test-authors.md
+++ b/ravenwood/test-authors.md
@@ -17,6 +17,7 @@
name: "MyTestsRavenwood",
static_libs: [
"androidx.annotation_annotation",
+ "androidx.test.ext.junit",
"androidx.test.rules",
],
srcs: [
@@ -34,7 +35,7 @@
import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/services/Android.bp b/services/Android.bp
index 6235195..cd974c5 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -324,34 +324,34 @@
baseline_file: "api/lint-baseline.txt",
},
},
- dists: [
- {
- targets: ["sdk"],
- dir: "apistubs/android/system-server/api",
- dest: "android-non-updatable.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/system-server/api",
- dest: "android-non-updatable-removed.txt",
- },
- ],
soong_config_variables: {
release_hidden_api_exportable_stubs: {
dists: [
{
+ targets: ["sdk"],
+ dir: "apistubs/android/system-server/api",
+ dest: "android-non-updatable.txt",
tag: ".exportable.api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/system-server/api",
+ dest: "android-non-updatable-removed.txt",
tag: ".exportable.removed-api.txt",
},
],
conditions_default: {
dists: [
{
+ targets: ["sdk"],
+ dir: "apistubs/android/system-server/api",
+ dest: "android-non-updatable.txt",
tag: ".api.txt",
},
{
+ targets: ["sdk"],
+ dir: "apistubs/android/system-server/api",
+ dest: "android-non-updatable-removed.txt",
tag: ".removed-api.txt",
},
],
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index bfa1c7b..8ab2e0f 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -56,6 +56,13 @@
}
flag {
+ name: "enable_hardware_shortcut_disables_warning"
+ namespace: "accessibility"
+ description: "When the user purposely enables the hardware shortcut, preemptively disables the first-time warning message."
+ bug: "287065325"
+}
+
+flag {
name: "enable_magnification_joystick"
namespace: "accessibility"
description: "Whether to enable joystick controls for magnification"
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 73584154..8a699ef 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1617,9 +1617,7 @@
final int displayId = displays[i].getDisplayId();
onDisplayRemoved(displayId);
}
- if (com.android.server.accessibility.Flags.cleanupA11yOverlays()) {
detachAllOverlays();
- }
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index bbbc4ae..ccf9a90 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -4175,8 +4175,13 @@
public void enableShortcutsForTargets(
boolean enable, @UserShortcutType int shortcutTypes,
@NonNull List<String> shortcutTargets, @UserIdInt int userId) {
- mContext.enforceCallingPermission(
- Manifest.permission.MANAGE_ACCESSIBILITY, "enableShortcutsForTargets");
+ if (android.view.accessibility.Flags.migrateEnableShortcuts()) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MANAGE_ACCESSIBILITY, "enableShortcutsForTargets");
+ } else {
+ mContext.enforceCallingPermission(
+ Manifest.permission.MANAGE_ACCESSIBILITY, "enableShortcutsForTargets");
+ }
for (int shortcutType : USER_SHORTCUT_TYPES) {
if ((shortcutTypes & shortcutType) == shortcutType) {
enableShortcutForTargets(enable, shortcutType, shortcutTargets, userId);
@@ -4268,6 +4273,13 @@
}
if (shortcutType == UserShortcutType.HARDWARE) {
skipVolumeShortcutDialogTimeoutRestriction(userId);
+ if (com.android.server.accessibility.Flags.enableHardwareShortcutDisablesWarning()) {
+ persistIntToSetting(
+ userId,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+ AccessibilityShortcutController.DialogStatus.SHOWN
+ );
+ }
} else if (shortcutType == UserShortcutType.SOFTWARE) {
// Update the A11y FAB size to large when the Magnification shortcut is
// enabled and the user hasn't changed the floating button size
diff --git a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
index b119d7d..853b824 100644
--- a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
+++ b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility;
import android.accessibilityservice.AccessibilityService;
+import android.annotation.RequiresNoPermission;
import android.os.Binder;
import android.os.RemoteException;
import android.util.Slog;
@@ -34,7 +35,6 @@
* If we are stripping and/or replacing the actions from a window, we need to intercept the
* nodes heading back to the service and swap out the actions.
*/
-@SuppressWarnings("MissingPermissionAnnotation")
public class ActionReplacingCallback extends IAccessibilityInteractionConnectionCallback.Stub {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "ActionReplacingCallback";
@@ -97,6 +97,7 @@
}
@Override
+ @RequiresNoPermission
public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info, int interactionId) {
synchronized (mLock) {
if (interactionId == mInteractionId) {
@@ -114,6 +115,7 @@
}
@Override
+ @RequiresNoPermission
public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
int interactionId) {
synchronized (mLock) {
@@ -132,6 +134,7 @@
}
@Override
+ @RequiresNoPermission
public void setPrefetchAccessibilityNodeInfoResult(List<AccessibilityNodeInfo> infos,
int interactionId)
throws RemoteException {
@@ -163,6 +166,7 @@
}
@Override
+ @RequiresNoPermission
public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId)
throws RemoteException {
// There's no reason to use this class when performing actions. Do something reasonable.
@@ -170,6 +174,7 @@
}
@Override
+ @RequiresNoPermission
public void sendTakeScreenshotOfWindowError(int errorCode, int interactionId)
throws RemoteException {
mServiceCallback.sendTakeScreenshotOfWindowError(errorCode, interactionId);
@@ -285,6 +290,7 @@
}
@Override
+ @RequiresNoPermission
public void sendAttachOverlayResult(
@AccessibilityService.AttachOverlayResult int result, int interactionId)
throws RemoteException {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index be2ad21..d279bd5 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -852,11 +852,9 @@
final int pointerIdBits = (1 << pointerId);
if (mSendHoverEnterAndMoveDelayed.isPending()) {
// If we have not delivered the enter schedule an exit.
- if (Flags.resetHoverEventTimerOnActionUp()) {
- // We cancel first to reset the time window so that the user has the full amount of
- // time to do a multi tap.
- mSendHoverEnterAndMoveDelayed.repost();
- }
+ // We cancel first to reset the time window so that the user has the full amount of
+ // time to do a multi tap.
+ mSendHoverEnterAndMoveDelayed.repost();
mSendHoverExitDelayed.post(event, rawEvent, pointerIdBits, policyFlags);
} else {
// The user is touch exploring so we send events for end.
@@ -1601,7 +1599,7 @@
+ " pointers down.");
return;
}
- if (Flags.resetHoverEventTimerOnActionUp() && mEvents.size() == 0) {
+ if (mEvents.size() == 0) {
return;
}
// Send an accessibility event to announce the touch exploration start.
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index ced10fb..70ecc05 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -41,3 +41,10 @@
description: "Use weak reference to address binder leak problem"
bug: "307972253"
}
+
+flag {
+ name: "include_last_focused_id_and_session_id_in_client_state"
+ namespace: "autofill"
+ description: "Include the current view id and session id into the FillEventHistory as part of ClientState"
+ bug: "334141398"
+}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index c96688c..7ceb3bb 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.content.IntentSender;
import android.os.Handler;
+import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.service.autofill.AutofillService;
@@ -42,7 +43,6 @@
import android.service.autofill.SaveRequest;
import android.text.format.DateUtils;
import android.util.Slog;
-import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.infra.ServiceConnector;
@@ -283,8 +283,7 @@
return callback;
}
- public void onFillCredentialRequest(@NonNull FillRequest request,
- IAutoFillManagerClient autofillCallback) {
+ public void onFillCredentialRequest(@NonNull FillRequest request, IBinder autofillCallback) {
if (sVerbose) {
Slog.v(TAG, "onFillRequest:" + request);
}
diff --git a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
index 4506779..044a064 100644
--- a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
+++ b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
@@ -16,21 +16,16 @@
package com.android.server.autofill;
-import static com.android.server.autofill.Session.REQUEST_ID_KEY;
-import static com.android.server.autofill.Session.SESSION_ID_KEY;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentSender;
-import android.os.Bundle;
+import android.os.IBinder;
import android.service.autofill.ConvertCredentialResponse;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.util.Slog;
-import android.view.autofill.IAutoFillManagerClient;
-import android.view.inputmethod.InlineSuggestionsRequest;
/**
* Requests autofill response from a Remote Autofill Service. This autofill service can be
@@ -55,6 +50,7 @@
private final RemoteFillService mRemoteFillService;
private final SecondaryProviderCallback mCallback;
+
private int mLastFlag;
SecondaryProviderHandler(
@@ -109,37 +105,17 @@
/**
* Requests a new fill response.
*/
- public void onFillRequest(FillRequest pendingFillRequest,
- InlineSuggestionsRequest pendingInlineSuggestionsRequest, int flag, int id,
- IAutoFillManagerClient client) {
+ public void onFillRequest(FillRequest pendingFillRequest, int flag, IBinder client) {
Slog.v(TAG, "Requesting fill response to secondary provider.");
mLastFlag = flag;
if (mRemoteFillService != null && mRemoteFillService.isCredentialAutofillService()) {
Slog.v(TAG, "About to call CredAutofill service as secondary provider");
- FillRequest request = addSessionIdAndRequestIdToClientState(pendingFillRequest,
- pendingInlineSuggestionsRequest, id);
- mRemoteFillService.onFillCredentialRequest(request, client);
+ mRemoteFillService.onFillCredentialRequest(pendingFillRequest, client);
} else {
mRemoteFillService.onFillRequest(pendingFillRequest);
}
}
- private FillRequest addSessionIdAndRequestIdToClientState(FillRequest pendingFillRequest,
- InlineSuggestionsRequest pendingInlineSuggestionsRequest, int sessionId) {
- if (pendingFillRequest.getClientState() == null) {
- pendingFillRequest = new FillRequest(pendingFillRequest.getId(),
- pendingFillRequest.getFillContexts(),
- pendingFillRequest.getHints(),
- new Bundle(),
- pendingFillRequest.getFlags(),
- pendingInlineSuggestionsRequest,
- pendingFillRequest.getDelayedFillIntentSender());
- }
- pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, sessionId);
- pendingFillRequest.getClientState().putInt(REQUEST_ID_KEY, pendingFillRequest.getId());
- return pendingFillRequest;
- }
-
public void destroy() {
mRemoteFillService.destroy();
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0b68f5f..cd1ef88 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -117,6 +117,7 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ServiceInfo;
+import android.credentials.CredentialManager;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialResponse;
import android.graphics.Bitmap;
@@ -252,7 +253,6 @@
private final AutofillManagerServiceImpl mService;
private final Handler mHandler;
private final AutoFillUI mUi;
-
/**
* Context associated with the session, it has the same {@link Context#getDisplayId() displayId}
* of the activity being autofilled.
@@ -751,14 +751,20 @@
if (shouldRequestSecondaryProvider(mPendingFillRequest.getFlags())
&& mSecondaryProviderHandler != null) {
Slog.v(TAG, "Requesting fill response to secondary provider.");
+ if (!mIsPrimaryCredential) {
+ mPendingFillRequest = addCredentialManagerDataToClientState(
+ mPendingFillRequest,
+ mPendingInlineSuggestionsRequest, id);
+ }
mSecondaryProviderHandler.onFillRequest(mPendingFillRequest,
- mPendingInlineSuggestionsRequest,
- mPendingFillRequest.getFlags(), id, mClient);
+ mPendingFillRequest.getFlags(), mClient.asBinder());
} else if (mRemoteFillService != null) {
if (mIsPrimaryCredential) {
- mPendingFillRequest = addSessionIdAndRequestIdToClientState(mPendingFillRequest,
+ mPendingFillRequest = addCredentialManagerDataToClientState(
+ mPendingFillRequest,
mPendingInlineSuggestionsRequest, id);
- mRemoteFillService.onFillCredentialRequest(mPendingFillRequest, mClient);
+ mRemoteFillService.onFillCredentialRequest(mPendingFillRequest,
+ mClient.asBinder());
} else {
mRemoteFillService.onFillRequest(mPendingFillRequest);
}
@@ -904,8 +910,9 @@
}
}
- private FillRequest addSessionIdAndRequestIdToClientState(FillRequest pendingFillRequest,
+ private FillRequest addCredentialManagerDataToClientState(FillRequest pendingFillRequest,
InlineSuggestionsRequest pendingInlineSuggestionsRequest, int sessionId) {
+
if (pendingFillRequest.getClientState() == null) {
pendingFillRequest = new FillRequest(pendingFillRequest.getId(),
pendingFillRequest.getFillContexts(),
@@ -917,6 +924,10 @@
}
pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, sessionId);
pendingFillRequest.getClientState().putInt(REQUEST_ID_KEY, pendingFillRequest.getId());
+ ResultReceiver resultReceiver = constructCredentialManagerCallback(
+ pendingFillRequest.getId());
+ pendingFillRequest.getClientState().putParcelable(
+ CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, resultReceiver);
return pendingFillRequest;
}
@@ -2887,7 +2898,7 @@
+ ", clientState=" + newClientState + ", authenticationId=" + authenticationId);
}
if (Flags.autofillCredmanDevIntegration() && exception != null
- && exception instanceof GetCredentialException) {
+ && !exception.getType().equals(GetCredentialException.TYPE_USER_CANCELED)) {
if (dataset != null && dataset.getFieldIds().size() == 1) {
if (sDebug) {
Slog.d(TAG, "setAuthenticationResultLocked(): result returns with"
@@ -4870,10 +4881,6 @@
}
- if (isCredmanIntegrationActive(response)) {
- addCredentialManagerCallback(response);
- }
-
if (response.supportsInlineSuggestions()) {
synchronized (mLock) {
if (requestShowInlineSuggestionsLocked(response, filterText)) {
@@ -5153,30 +5160,14 @@
return mInlineSessionController.setInlineFillUiLocked(inlineFillUi);
}
- private void addCredentialManagerCallback(FillResponse response) {
- if (response.getDatasets() == null) {
- return;
- }
- for (Dataset dataset: response.getDatasets()) {
- if (dataset.getId() != null
- && dataset.getId().equals(AutofillManager.PINNED_DATASET_ID)) {
- Slog.d(TAG, "Adding Credential Manager callback to a pinned entry");
- addCredentialManagerCallbackForDataset(dataset, response.getRequestId());
- }
- }
- }
-
- private void addCredentialManagerCallbackForDataset(Dataset dataset, int requestId) {
- AutofillId autofillId = null;
- if (dataset != null && dataset.getFieldIds().size() == 1) {
- autofillId = dataset.getFieldIds().get(0);
- }
- final AutofillId finalAutofillId = autofillId;
+ private ResultReceiver constructCredentialManagerCallback(int requestId) {
final ResultReceiver resultReceiver = new ResultReceiver(mHandler) {
+ final AutofillId mAutofillId = mCurrentViewId;
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == SUCCESS_CREDMAN_SELECTOR) {
- Slog.d(TAG, "onReceiveResult from Credential Manager bottom sheet");
+ Slog.d(TAG, "onReceiveResult from Credential Manager "
+ + "bottom sheet with mCurrentViewId: " + mAutofillId);
GetCredentialResponse getCredentialResponse =
resultData.getParcelable(
CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
@@ -5184,7 +5175,7 @@
if (Flags.autofillCredmanDevIntegration()) {
sendCredentialManagerResponseToApp(getCredentialResponse,
- /*exception=*/ null, finalAutofillId);
+ /*exception=*/ null, mAutofillId);
} else {
Dataset datasetFromCredential = getDatasetFromCredentialResponse(
getCredentialResponse);
@@ -5198,12 +5189,14 @@
String[] exception = resultData.getStringArray(
CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION);
if (exception != null && exception.length >= 2) {
+ String errType = exception[0];
+ String errMsg = exception[1];
Slog.w(TAG, "Credman bottom sheet from pinned "
- + "entry failed with: + " + exception[0] + " , "
- + exception[1]);
+ + "entry failed with: + " + errType + " , "
+ + errMsg);
sendCredentialManagerResponseToApp(/*response=*/ null,
- new GetCredentialException(exception[0], exception[1]),
- finalAutofillId);
+ new GetCredentialException(errType, errMsg),
+ mAutofillId);
}
} else {
Slog.d(TAG, "Unknown resultCode from credential "
@@ -5214,15 +5207,7 @@
ResultReceiver ipcFriendlyResultReceiver =
toIpcFriendlyResultReceiver(resultReceiver);
- Intent metadataIntent = dataset.getCredentialFillInIntent();
- if (metadataIntent == null) {
- metadataIntent = new Intent();
- }
-
- metadataIntent.putExtra(
- android.credentials.selection.Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
- ipcFriendlyResultReceiver);
- dataset.setCredentialFillInIntent(metadataIntent);
+ return ipcFriendlyResultReceiver;
}
private ResultReceiver toIpcFriendlyResultReceiver(ResultReceiver resultReceiver) {
@@ -6510,21 +6495,15 @@
}
}
if (exception != null) {
- mClient.onGetCredentialException(id, viewId, exception.getType(),
- exception.getMessage());
+ if (viewId.isVirtualInt()) {
+ sendResponseToViewNode(viewId, /*response=*/ null, exception);
+ } else {
+ mClient.onGetCredentialException(id, viewId, exception.getType(),
+ exception.getMessage());
+ }
} else if (response != null) {
if (viewId.isVirtualInt()) {
- ViewNode viewNode = getViewNodeFromContextsLocked(viewId);
- if (viewNode != null && viewNode.getPendingCredentialCallback() != null) {
- Bundle resultData = new Bundle();
- resultData.putParcelable(
- CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
- response);
- viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR,
- resultData);
- } else {
- Slog.w(TAG, "View node not found after GetCredentialResponse");
- }
+ sendResponseToViewNode(viewId, response, /*exception=*/ null);
} else {
mClient.onGetCredentialResponse(id, viewId, response);
}
@@ -6538,6 +6517,30 @@
}
}
+ @GuardedBy("mLock")
+ private void sendResponseToViewNode(AutofillId viewId, GetCredentialResponse response,
+ GetCredentialException exception) {
+ ViewNode viewNode = getViewNodeFromContextsLocked(viewId);
+ if (viewNode != null && viewNode.getPendingCredentialCallback() != null) {
+ Bundle resultData = new Bundle();
+ if (response != null) {
+ resultData.putParcelable(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
+ response);
+ viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR,
+ resultData);
+ } else if (exception != null) {
+ resultData.putStringArray(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
+ new String[] {exception.getType(), exception.getMessage()});
+ viewNode.getPendingCredentialCallback().send(FAILURE_CREDMAN_SELECTOR,
+ resultData);
+ }
+ } else {
+ Slog.w(TAG, "View node not found after GetCredentialResponse");
+ }
+ }
+
void autoFillApp(Dataset dataset) {
synchronized (mLock) {
if (mDestroyed) {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index ce9cdc2..0353d5a 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1510,46 +1510,74 @@
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
return;
}
- dumpWithoutCheckingPermission(fd, pw, args);
- }
- @VisibleForTesting
- void dumpWithoutCheckingPermission(FileDescriptor fd, PrintWriter pw, String[] args) {
- int userId = binderGetCallingUserId();
- if (!isUserReadyForBackup(userId)) {
- pw.println("Inactive");
+ int argIndex = 0;
+
+ String op = nextArg(args, argIndex);
+ argIndex++;
+
+ if ("--help".equals(op)) {
+ showDumpUsage(pw);
return;
}
-
- if (args != null) {
- for (String arg : args) {
- if ("-h".equals(arg)) {
- pw.println("'dumpsys backup' optional arguments:");
- pw.println(" -h : this help text");
- pw.println(" a[gents] : dump information about defined backup agents");
- pw.println(" transportclients : dump information about transport clients");
- pw.println(" transportstats : dump transport statts");
- pw.println(" users : dump the list of users for which backup service "
- + "is running");
- return;
- } else if ("users".equals(arg.toLowerCase())) {
- pw.print(DUMP_RUNNING_USERS_MESSAGE);
- for (int i = 0; i < mUserServices.size(); i++) {
- pw.print(" " + mUserServices.keyAt(i));
- }
- pw.println();
- return;
+ if ("users".equals(op)) {
+ pw.print(DUMP_RUNNING_USERS_MESSAGE);
+ for (int i = 0; i < mUserServices.size(); i++) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(mUserServices.keyAt(i),
+ "dump()");
+ if (userBackupManagerService != null) {
+ pw.print(" " + userBackupManagerService.getUserId());
}
}
+ pw.println();
+ return;
}
-
- for (int i = 0; i < mUserServices.size(); i++) {
+ if ("--user".equals(op)) {
+ String userArg = nextArg(args, argIndex);
+ argIndex++;
+ if (userArg == null) {
+ showDumpUsage(pw);
+ return;
+ }
+ int userId = UserHandle.parseUserArg(userArg);
UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(mUserServices.keyAt(i), "dump()");
+ getServiceForUserIfCallerHasPermission(userId, "dump()");
if (userBackupManagerService != null) {
userBackupManagerService.dump(fd, pw, args);
}
+ return;
}
+ if (op == null || "agents".startsWith(op) || "transportclients".equals(op)
+ || "transportstats".equals(op)) {
+ for (int i = 0; i < mUserServices.size(); i++) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(mUserServices.keyAt(i), "dump()");
+ if (userBackupManagerService != null) {
+ userBackupManagerService.dump(fd, pw, args);
+ }
+ }
+ return;
+ }
+
+ showDumpUsage(pw);
+ }
+
+ private String nextArg(String[] args, int argIndex) {
+ if (argIndex >= args.length) {
+ return null;
+ }
+ return args[argIndex];
+ }
+
+ private static void showDumpUsage(PrintWriter pw) {
+ pw.println("'dumpsys backup' optional arguments:");
+ pw.println(" --help : this help text");
+ pw.println(" a[gents] : dump information about defined backup agents");
+ pw.println(" transportclients : dump information about transport clients");
+ pw.println(" transportstats : dump transport stats");
+ pw.println(" users : dump the list of users for which backup service is running");
+ pw.println(" --user <userId> : dump information for user userId");
}
/**
@@ -1661,7 +1689,7 @@
* @param message A message to include in the exception if it is thrown.
*/
void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
- if (Binder.getCallingUserHandle().getIdentifier() != userId) {
+ if (binderGetCallingUserId() != userId) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
}
diff --git a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
index cfb7f337..af49df6 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
@@ -56,6 +56,7 @@
import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.CollectionUtils;
import com.android.server.companion.association.AssociationStore;
import java.io.PrintWriter;
@@ -1031,6 +1032,9 @@
public void sendDevicePresenceEventOnUnlocked(int userId) {
final List<DevicePresenceEvent> deviceEvents = getPendingDevicePresenceEventsByUserId(
userId);
+ if (CollectionUtils.isEmpty(deviceEvents)) {
+ return;
+ }
final List<ObservableUuid> observableUuids =
mObservableUuidStore.getObservableUuidsForUser(userId);
// Notify and bind the app after the phone is unlocked.
@@ -1068,7 +1072,7 @@
}
}
- clearPendingDevicePresenceEventsByUserId(userId);
+ removePendingDevicePresenceEventsByUserId(userId);
}
private List<DevicePresenceEvent> getPendingDevicePresenceEventsByUserId(int userId) {
@@ -1077,9 +1081,11 @@
}
}
- private void clearPendingDevicePresenceEventsByUserId(int userId) {
+ private void removePendingDevicePresenceEventsByUserId(int userId) {
synchronized (mPendingDevicePresenceEvents) {
- mPendingDevicePresenceEvents.get(userId).clear();
+ if (mPendingDevicePresenceEvents.contains(userId)) {
+ mPendingDevicePresenceEvents.remove(userId);
+ }
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 23373f1..afeafa4 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -302,7 +302,7 @@
if (Flags.interceptIntentsBeforeApplyingPolicy()) {
if (mIntentListenerCallback != null && intent != null
&& mIntentListenerCallback.shouldInterceptIntent(intent)) {
- Slog.d(TAG, "Virtual device intercepting intent");
+ logActivityLaunchBlocked("Virtual device intercepting intent");
return false;
}
if (!canContainActivity(activityInfo, windowingMode, launchingFromDisplayId,
@@ -318,7 +318,7 @@
}
if (mIntentListenerCallback != null && intent != null
&& mIntentListenerCallback.shouldInterceptIntent(intent)) {
- Slog.d(TAG, "Virtual device intercepting intent");
+ logActivityLaunchBlocked("Virtual device intercepting intent");
return false;
}
}
@@ -331,15 +331,17 @@
boolean isNewTask) {
// Mirror displays cannot contain activities.
if (waitAndGetIsMirrorDisplay()) {
- Slog.d(TAG, "Mirror virtual displays cannot contain activities.");
+ logActivityLaunchBlocked("Mirror virtual displays cannot contain activities.");
return false;
}
if (!isWindowingModeSupported(windowingMode)) {
- Slog.d(TAG, "Virtual device doesn't support windowing mode " + windowingMode);
+ logActivityLaunchBlocked(
+ "Virtual device doesn't support windowing mode " + windowingMode);
return false;
}
if ((activityInfo.flags & FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
- Slog.d(TAG, "Virtual device requires android:canDisplayOnRemoteDevices=true");
+ logActivityLaunchBlocked(
+ "Activity requires android:canDisplayOnRemoteDevices=true");
return false;
}
final UserHandle activityUser =
@@ -350,11 +352,11 @@
return true;
}
if (!activityUser.isSystem() && !mAllowedUsers.contains(activityUser)) {
- Slog.d(TAG, "Virtual device launch disallowed from user " + activityUser);
+ logActivityLaunchBlocked("Activity launch disallowed from user " + activityUser);
return false;
}
if (!activityMatchesDisplayCategory(activityInfo)) {
- Slog.d(TAG, "The activity's required display category '"
+ logActivityLaunchBlocked("The activity's required display category '"
+ activityInfo.requiredDisplayCategory
+ "' not found on virtual display with the following categories: "
+ mDisplayCategories);
@@ -363,7 +365,7 @@
synchronized (mGenericWindowPolicyControllerLock) {
if (!isAllowedByPolicy(mActivityLaunchAllowedByDefault, mActivityPolicyExemptions,
activityComponent)) {
- Slog.d(TAG, "Virtual device launch disallowed by policy: "
+ logActivityLaunchBlocked("Activity launch disallowed by policy: "
+ activityComponent);
return false;
}
@@ -371,7 +373,7 @@
if (isNewTask && launchingFromDisplayId != DEFAULT_DISPLAY
&& !isAllowedByPolicy(mCrossTaskNavigationAllowedByDefault,
mCrossTaskNavigationExemptions, activityComponent)) {
- Slog.d(TAG, "Virtual device cross task navigation disallowed by policy: "
+ logActivityLaunchBlocked("Cross task navigation disallowed by policy: "
+ activityComponent);
return false;
}
@@ -380,12 +382,18 @@
// based on FLAG_STREAM_PERMISSIONS
if (mPermissionDialogComponent != null
&& mPermissionDialogComponent.equals(activityComponent)) {
+ logActivityLaunchBlocked("Permission dialog not allowed on virtual device");
return false;
}
return true;
}
+ private void logActivityLaunchBlocked(String reason) {
+ Slog.d(TAG, "Virtual device activity launch disallowed on display "
+ + waitAndGetDisplayId() + ", reason: " + reason);
+ }
+
@Override
@SuppressWarnings("AndroidFrameworkRequiresPermission")
public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 9b72288..3c25835 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -226,7 +226,7 @@
token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
mNativeWrapper.closeUinput(inputDeviceDescriptor.getNativePointer());
String phys = inputDeviceDescriptor.getPhys();
- InputManagerGlobal.getInstance().removeUniqueIdAssociation(phys);
+ InputManagerGlobal.getInstance().removeUniqueIdAssociationByDescriptor(phys);
// Type associations are added in the case of navigation touchpads. Those should be removed
// once the input device gets closed.
if (inputDeviceDescriptor.getType() == InputDeviceDescriptor.TYPE_NAVIGATION_TOUCHPAD) {
@@ -319,9 +319,9 @@
return formatSimple("virtual%s:%d", type, sNextPhysId.getAndIncrement());
}
- private void setUniqueIdAssociation(int displayId, String phys) {
+ private void setUniqueIdAssociationByPort(int displayId, String phys) {
final String displayUniqueId = mDisplayManagerInternal.getDisplayInfo(displayId).uniqueId;
- InputManagerGlobal.getInstance().addUniqueIdAssociation(phys, displayUniqueId);
+ InputManagerGlobal.getInstance().addUniqueIdAssociationByPort(phys, displayUniqueId);
}
boolean sendDpadKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) {
@@ -809,7 +809,7 @@
final int inputDeviceId;
- setUniqueIdAssociation(displayId, phys);
+ setUniqueIdAssociationByPort(displayId, phys);
try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId, displayId)) {
ptr = deviceOpener.get();
// See INVALID_PTR in libs/input/VirtualInputDevice.cpp.
@@ -835,7 +835,7 @@
throw e;
}
} catch (DeviceCreationException e) {
- InputManagerGlobal.getInstance().removeUniqueIdAssociation(phys);
+ InputManagerGlobal.getInstance().removeUniqueIdAssociationByPort(phys);
throw e;
}
diff --git a/packages/CrashRecovery/services/java/com/android/server/ExplicitHealthCheckController.java b/services/core/java/com/android/server/ExplicitHealthCheckController.java
similarity index 100%
rename from packages/CrashRecovery/services/java/com/android/server/ExplicitHealthCheckController.java
rename to services/core/java/com/android/server/ExplicitHealthCheckController.java
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 1741593..ccc44a4 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static com.android.internal.R.integer.config_defaultMinEmergencyGestureTapDurationMillis;
+
import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
@@ -70,12 +72,6 @@
*/
@VisibleForTesting static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
- /**
- * Min time in milliseconds to complete the emergency gesture for it count. If the gesture is
- * completed faster than this, we assume it's not performed by human and the
- * event gets ignored.
- */
- @VisibleForTesting static final int EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS = 200;
/**
* Interval in milliseconds in which the power button must be depressed in succession to be
@@ -570,7 +566,8 @@
long emergencyGestureTapDetectionMinTimeMs = Settings.Global.getInt(
mContext.getContentResolver(),
Settings.Global.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS,
- EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS);
+ mContext.getResources().getInteger(
+ config_defaultMinEmergencyGestureTapDurationMillis));
if (emergencyGestureSpentTime <= emergencyGestureTapDetectionMinTimeMs) {
Slog.i(TAG, "Emergency gesture detected but it's too fast. Gesture time: "
+ emergencyGestureSpentTime + " ms");
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index bdc4a7a..2545620 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -22,6 +22,7 @@
per-file *Battery* = file:/BATTERY_STATS_OWNERS
per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS
per-file *Binder* = file:/core/java/com/android/internal/os/BINDER_OWNERS
+per-file ExplicitHealthCheckController.java = file:/services/core/java/com/android/server/crashrecovery/OWNERS
per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
per-file **IpSec* = file:/services/core/java/com/android/server/net/OWNERS
per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
@@ -35,9 +36,9 @@
per-file GestureLauncherService.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
per-file MmsServiceBroker.java = file:/telephony/OWNERS
per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS
-per-file PackageWatchdog.java, RescueParty.java = file:/services/core/java/com/android/server/rollback/OWNERS
+per-file PackageWatchdog.java = file:/services/core/java/com/android/server/crashrecovery/OWNERS
per-file PinnerService.java = file:/core/java/android/app/pinner/OWNERS
-per-file RescueParty.java = shuc@google.com, ancr@google.com, harshitmahajan@google.com
+per-file RescueParty.java = file:/services/core/java/com/android/server/crashrecovery/OWNERS
per-file SensitiveContentProtectionManagerService.java = file:/core/java/android/permission/OWNERS
per-file SystemClockTime.java = file:/services/core/java/com/android/server/timedetector/OWNERS
per-file SystemTimeZone.java = file:/services/core/java/com/android/server/timezonedetector/OWNERS
diff --git a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
similarity index 99%
rename from packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
rename to services/core/java/com/android/server/PackageWatchdog.java
index 8891b50..6f20adf 100644
--- a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -39,13 +39,13 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
-import android.util.BackgroundThread;
import android.util.LongArrayQueue;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
diff --git a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
similarity index 100%
rename from packages/CrashRecovery/services/java/com/android/server/RescueParty.java
rename to services/core/java/com/android/server/RescueParty.java
diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
index 4694e9f..6c7546e 100644
--- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
+++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.permission.flags.Flags.sensitiveContentImprovements;
import static android.permission.flags.Flags.sensitiveNotificationAppProtection;
import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;
import static android.view.flags.Flags.sensitiveContentAppProtection;
@@ -24,6 +25,7 @@
import static com.android.internal.util.FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__FRAMEWORKS;
import static com.android.internal.util.FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START;
import static com.android.internal.util.FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP;
+import static com.android.internal.util.FrameworkStatsLog.SENSITIVE_NOTIFICATION_APP_PROTECTION_SESSION;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -91,9 +93,11 @@
private boolean mProjectionActive = false;
private static class MediaProjectionSession {
- final int mUid;
- final long mSessionId;
- final boolean mIsExempted;
+ private final int mUid;
+ private final long mSessionId;
+ private final boolean mIsExempted;
+ private final ArraySet<String> mAllSeenNotificationKeys = new ArraySet<>();
+ private final ArraySet<String> mSeenOtpNotificationKeys = new ArraySet<>();
MediaProjectionSession(int uid, boolean isExempted, long sessionId) {
mUid = uid;
@@ -123,6 +127,14 @@
);
}
+ public void logAppNotificationsProtected() {
+ FrameworkStatsLog.write(
+ SENSITIVE_NOTIFICATION_APP_PROTECTION_SESSION,
+ mSessionId,
+ mAllSeenNotificationKeys.size(),
+ mSeenOtpNotificationKeys.size());
+ }
+
public void logAppBlocked(int uid) {
FrameworkStatsLog.write(
FrameworkStatsLog.SENSITIVE_CONTENT_APP_PROTECTION,
@@ -142,6 +154,32 @@
FrameworkStatsLog.SENSITIVE_CONTENT_APP_PROTECTION__STATE__UNBLOCKED
);
}
+
+ private void addSeenNotificationKey(String key) {
+ mAllSeenNotificationKeys.add(key);
+ }
+
+ private void addSeenOtpNotificationKey(String key) {
+ mAllSeenNotificationKeys.add(key);
+ mSeenOtpNotificationKeys.add(key);
+ }
+
+ public void addSeenNotifications(
+ @NonNull StatusBarNotification[] notifications,
+ @NonNull RankingMap rankingMap) {
+ for (StatusBarNotification sbn : notifications) {
+ if (sbn == null) {
+ Log.w(TAG, "Unable to parse null notification");
+ continue;
+ }
+
+ if (notificationHasSensitiveContent(sbn, rankingMap)) {
+ addSeenOtpNotificationKey(sbn.getKey());
+ } else {
+ addSeenNotificationKey(sbn.getKey());
+ }
+ }
+ }
}
private final MediaProjectionManager.Callback mProjectionCallback =
@@ -297,6 +335,9 @@
mProjectionActive = false;
if (mMediaProjectionSession != null) {
mMediaProjectionSession.logProjectionSessionStop();
+ if (sensitiveContentImprovements()) {
+ mMediaProjectionSession.logAppNotificationsProtected();
+ }
mMediaProjectionSession = null;
}
@@ -306,6 +347,7 @@
}
}
+ @GuardedBy("mSensitiveContentProtectionLock")
private void updateAppsThatShouldBlockScreenCapture() {
RankingMap rankingMap;
try {
@@ -315,10 +357,16 @@
rankingMap = null;
}
+ if (rankingMap == null) {
+ Log.w(TAG, "Ranking map not initialized.");
+ return;
+ }
+
updateAppsThatShouldBlockScreenCapture(rankingMap);
}
- private void updateAppsThatShouldBlockScreenCapture(RankingMap rankingMap) {
+ @GuardedBy("mSensitiveContentProtectionLock")
+ private void updateAppsThatShouldBlockScreenCapture(@NonNull RankingMap rankingMap) {
StatusBarNotification[] notifications;
try {
notifications = mNotificationListener.getActiveNotifications();
@@ -327,23 +375,28 @@
notifications = new StatusBarNotification[0];
}
+ if (sensitiveContentImprovements() && mMediaProjectionSession != null) {
+ mMediaProjectionSession.addSeenNotifications(notifications, rankingMap);
+ }
+
// notify windowmanager of any currently posted sensitive content notifications
ArraySet<PackageInfo> packageInfos =
getSensitivePackagesFromNotifications(notifications, rankingMap);
+
if (packageInfos.size() > 0) {
mWindowManager.addBlockScreenCaptureForApps(packageInfos);
}
}
- private ArraySet<PackageInfo> getSensitivePackagesFromNotifications(
- @NonNull StatusBarNotification[] notifications, RankingMap rankingMap) {
+ private static @NonNull ArraySet<PackageInfo> getSensitivePackagesFromNotifications(
+ @NonNull StatusBarNotification[] notifications, @NonNull RankingMap rankingMap) {
ArraySet<PackageInfo> sensitivePackages = new ArraySet<>();
- if (rankingMap == null) {
- Log.w(TAG, "Ranking map not initialized.");
- return sensitivePackages;
- }
-
for (StatusBarNotification sbn : notifications) {
+ if (sbn == null) {
+ Log.w(TAG, "Unable to parse null notification");
+ continue;
+ }
+
PackageInfo info = getSensitivePackageFromNotification(sbn, rankingMap);
if (info != null) {
sensitivePackages.add(info);
@@ -352,24 +405,20 @@
return sensitivePackages;
}
- private PackageInfo getSensitivePackageFromNotification(
- StatusBarNotification sbn, RankingMap rankingMap) {
- if (sbn == null) {
- Log.w(TAG, "Unable to protect null notification");
- return null;
- }
- if (rankingMap == null) {
- Log.w(TAG, "Ranking map not initialized.");
- return null;
- }
-
- NotificationListenerService.Ranking ranking = rankingMap.getRawRankingObject(sbn.getKey());
- if (ranking != null && ranking.hasSensitiveContent()) {
+ private static @Nullable PackageInfo getSensitivePackageFromNotification(
+ @NonNull StatusBarNotification sbn, @NonNull RankingMap rankingMap) {
+ if (notificationHasSensitiveContent(sbn, rankingMap)) {
return new PackageInfo(sbn.getPackageName(), sbn.getUid());
}
return null;
}
+ private static boolean notificationHasSensitiveContent(
+ @NonNull StatusBarNotification sbn, @NonNull RankingMap rankingMap) {
+ NotificationListenerService.Ranking ranking = rankingMap.getRawRankingObject(sbn.getKey());
+ return ranking != null && ranking.hasSensitiveContent();
+ }
+
@VisibleForTesting
class NotificationListener extends NotificationListenerService {
@Override
@@ -395,6 +444,16 @@
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
"SensitiveContentProtectionManagerService.onNotificationPosted");
try {
+ if (sbn == null) {
+ Log.w(TAG, "Unable to parse null notification");
+ return;
+ }
+
+ if (rankingMap == null) {
+ Log.w(TAG, "Ranking map not initialized.");
+ return;
+ }
+
synchronized (mSensitiveContentProtectionLock) {
if (!mProjectionActive) {
return;
@@ -407,6 +466,14 @@
mWindowManager.addBlockScreenCaptureForApps(
new ArraySet(Set.of(packageInfo)));
}
+
+ if (sensitiveContentImprovements() && mMediaProjectionSession != null) {
+ if (packageInfo != null) {
+ mMediaProjectionSession.addSeenOtpNotificationKey(sbn.getKey());
+ } else {
+ mMediaProjectionSession.addSeenNotificationKey(sbn.getKey());
+ }
+ }
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -419,6 +486,11 @@
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
"SensitiveContentProtectionManagerService.onNotificationRankingUpdate");
try {
+ if (rankingMap == null) {
+ Log.w(TAG, "Ranking map not initialized.");
+ return;
+ }
+
synchronized (mSensitiveContentProtectionLock) {
if (mProjectionActive) {
updateAppsThatShouldBlockScreenCapture(rankingMap);
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 20816a1..32c7dde 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -44,6 +44,9 @@
import com.android.server.pm.ApexManager;
import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.TypePattern;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
import dalvik.system.PathClassLoader;
@@ -135,7 +138,13 @@
}
/**
- * Starts a service by class name.
+ * Starts a service by class name from the current {@code SYSTEMSERVERCLASSPATH}.
+ *
+ * In general, this should only be used for services in the classpath that cannot
+ * be resolved by {@code services.jar} at build time, e.g., those defined in an apex jar from
+ * {@code PRODUCT_APEX_SYSTEM_SERVER_JARS} or a downstream jar in
+ * {@code PRODUCT_SYSTEM_SERVER_JARS}. Otherwise prefer the explicit type variant
+ * {@link #startService(Class)}.
*
* @return The service instance.
*/
@@ -147,7 +156,11 @@
}
/**
- * Starts a service by class name and a path that specifies the jar where the service lives.
+ * Starts a service by class name and standalone jar path where the service lives.
+ *
+ * In general, this should only be used for services in {@code STANDALONE_SYSTEMSERVER_JARS},
+ * which in turn derives from {@code PRODUCT_STANDALONE_SYSTEM_SERVER_JARS} and
+ * {@code PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS}.
*
* @return The service instance.
*/
@@ -207,6 +220,11 @@
* @throws RuntimeException if the service fails to start.
*/
@android.ravenwood.annotation.RavenwoodKeep
+ @UsesReflection(
+ @KeepTarget(
+ instanceOfClassConstantExclusive = SystemService.class,
+ methodName = "<init>",
+ methodParameterTypePatterns = {@TypePattern(constant = Context.class)}))
public <T extends SystemService> T startService(Class<T> serviceClass) {
try {
final String name = serviceClass.getName();
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 603a95c..1015ad9 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -880,6 +880,14 @@
packagesToVisibility = Collections.emptyMap();
accountRemovedReceivers = Collections.emptyList();
}
+ if (notify) {
+ Integer oldVisibility =
+ accounts.accountsDb.findAccountVisibility(account, packageName);
+ if (oldVisibility != null && oldVisibility == newVisibility) {
+ // Database will not be updated - skip LOGIN_ACCOUNTS_CHANGED broadcast.
+ notify = false;
+ }
+ }
if (!updateAccountVisibilityLocked(account, packageName, newVisibility, accounts)) {
return false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ad15ea9..9401a56 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -497,6 +497,10 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -629,6 +633,9 @@
private static final int MAX_BUGREPORT_TITLE_SIZE = 100;
private static final int MAX_BUGREPORT_DESCRIPTION_SIZE = 150;
+ private static final DateTimeFormatter DROPBOX_TIME_FORMATTER =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSZ");
+
OomAdjuster mOomAdjuster;
static final String EXTRA_TITLE = "android.intent.extra.TITLE";
@@ -2167,22 +2174,25 @@
*/
static class VolatileDropboxEntryStates {
private final Boolean mIsProcessFrozen;
+ private final ZonedDateTime mTimestamp;
- private VolatileDropboxEntryStates(Boolean frozenState) {
+ private VolatileDropboxEntryStates(Boolean frozenState, ZonedDateTime timestamp) {
this.mIsProcessFrozen = frozenState;
+ this.mTimestamp = timestamp;
}
- public static VolatileDropboxEntryStates withProcessFrozenState(boolean frozenState) {
- return new VolatileDropboxEntryStates(frozenState);
- }
-
- public static VolatileDropboxEntryStates emptyVolatileDropboxEnytyStates() {
- return new VolatileDropboxEntryStates(null);
+ public static VolatileDropboxEntryStates withProcessFrozenStateAndTimestamp(
+ boolean frozenState, ZonedDateTime timestamp) {
+ return new VolatileDropboxEntryStates(frozenState, timestamp);
}
public Boolean isProcessFrozen() {
return mIsProcessFrozen;
}
+
+ public ZonedDateTime getTimestamp() {
+ return mTimestamp;
+ }
}
static class MemBinder extends Binder {
@@ -4429,8 +4439,16 @@
final boolean clearPendingIntentsForStoppedApp = (android.content.pm.Flags.stayStopped()
&& packageStateStopped);
if (packageName == null || uninstalling || clearPendingIntentsForStoppedApp) {
+ final int cancelReason;
+ if (packageName == null) {
+ cancelReason = PendingIntentRecord.CANCEL_REASON_USER_STOPPED;
+ } else if (uninstalling) {
+ cancelReason = PendingIntentRecord.CANCEL_REASON_OWNER_UNINSTALLED;
+ } else {
+ cancelReason = PendingIntentRecord.CANCEL_REASON_OWNER_FORCE_STOPPED;
+ }
didSomething |= mPendingIntentController.removePendingIntentsForPackage(
- packageName, userId, appId, doit);
+ packageName, userId, appId, doit, cancelReason);
}
if (doit) {
@@ -5399,14 +5417,12 @@
}
}
- /**
- * Checks if feature flag is enabled and if system is Headless (HSUM), case in which
- * home delay should be skipped.
- *
- * @hide
- */
- public boolean isHomeLaunchDelayable() {
- return !UserManager.isHeadlessSystemUserMode() && enableHomeDelay();
+ /** Checks whether the home launch delay feature is enabled. */
+ private boolean isHomeLaunchDelayable() {
+ // This feature is disabled on Auto since it seems to add an unacceptably long boot delay
+ // without even solving the underlying issue (it merely hits the timeout).
+ return enableHomeDelay() &&
+ !mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
final void ensureBootCompleted() {
@@ -9173,6 +9189,11 @@
private class MyBinderProxyCountEventListener implements BinderProxyCountEventListener {
@Override
public void onLimitReached(int uid) {
+ // Spawn a new thread for the dump as it'll take long time.
+ new Thread(() -> handleLimitReached(uid), "BinderProxy Dump: " + uid).start();
+ }
+
+ private void handleLimitReached(int uid) {
Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
+ Process.myUid());
BinderProxy.dumpProxyDebugInfo();
@@ -9678,6 +9699,11 @@
? volatileStates.isProcessFrozen() : process.mOptRecord.isFrozen()
).append("\n");
}
+ if (volatileStates != null && volatileStates.getTimestamp() != null) {
+ String formattedTime = DROPBOX_TIME_FORMATTER.format(
+ volatileStates.getTimestamp());
+ sb.append("Timestamp: ").append(formattedTime).append("\n");
+ }
int flags = process.info.flags;
final IPackageManager pm = AppGlobals.getPackageManager();
sb.append("Flags: 0x").append(Integer.toHexString(flags)).append("\n");
@@ -9821,6 +9847,11 @@
sb.append("Process-Runtime: ").append(runtimeMillis).append("\n");
}
}
+ if (eventType.equals("crash")) {
+ String formattedTime = DROPBOX_TIME_FORMATTER.format(
+ Instant.now().atZone(ZoneId.systemDefault()));
+ sb.append("Timestamp: ").append(formattedTime).append("\n");
+ }
if (activityShortComponentName != null) {
sb.append("Activity: ").append(activityShortComponentName).append("\n");
}
@@ -10154,7 +10185,11 @@
}
final int callingUid = Binder.getCallingUid();
- mProcessList.getAppStartInfoTracker().addStartInfoCompleteListener(listener, callingUid);
+ mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId, true,
+ ALLOW_NON_FULL, "addApplicationStartInfoCompleteListener", null);
+
+ mProcessList.getAppStartInfoTracker().addStartInfoCompleteListener(listener,
+ UserHandle.getUid(userId, UserHandle.getAppId(callingUid)));
}
@@ -10169,13 +10204,30 @@
}
final int callingUid = Binder.getCallingUid();
- mProcessList.getAppStartInfoTracker().removeStartInfoCompleteListener(listener, callingUid,
- true);
+ mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId, true,
+ ALLOW_NON_FULL, "removeApplicationStartInfoCompleteListener", null);
+
+ mProcessList.getAppStartInfoTracker().removeStartInfoCompleteListener(listener,
+ UserHandle.getUid(userId, UserHandle.getAppId(callingUid)), true);
}
@Override
public void addStartInfoTimestamp(int key, long timestampNs, int userId) {
enforceNotIsolatedCaller("addStartInfoTimestamp");
+
+ // For the simplification, we don't support USER_ALL nor USER_CURRENT here.
+ if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_CURRENT) {
+ throw new IllegalArgumentException("Unsupported userId");
+ }
+
+ final int callingUid = Binder.getCallingUid();
+ mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId, true,
+ ALLOW_NON_FULL, "addStartInfoTimestamp", null);
+
+ final String packageName = Settings.getPackageNameForUid(mContext, callingUid);
+
+ mProcessList.getAppStartInfoTracker().addTimestampToStart(packageName,
+ UserHandle.getUid(userId, UserHandle.getAppId(callingUid)), timestampNs, key);
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index e70722c..372ec45 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -97,6 +97,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.Debug;
import android.os.IProgressListener;
import android.os.ParcelFileDescriptor;
import android.os.RemoteCallback;
@@ -125,6 +126,8 @@
import com.android.server.am.LowMemDetector.MemFactor;
import com.android.server.am.nano.Capabilities;
import com.android.server.am.nano.Capability;
+import com.android.server.am.nano.FrameworkCapability;
+import com.android.server.am.nano.VMCapability;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.Slogf;
@@ -443,6 +446,22 @@
capabilities.values[i] = cap;
}
+ String[] vmCapabilities = Debug.getVmFeatureList();
+ capabilities.vmCapabilities = new VMCapability[vmCapabilities.length];
+ for (int i = 0; i < vmCapabilities.length; i++) {
+ VMCapability cap = new VMCapability();
+ cap.name = vmCapabilities[i];
+ capabilities.vmCapabilities[i] = cap;
+ }
+
+ String[] fmCapabilities = Debug.getFeatureList();
+ capabilities.frameworkCapabilities = new FrameworkCapability[fmCapabilities.length];
+ for (int i = 0; i < fmCapabilities.length; i++) {
+ FrameworkCapability cap = new FrameworkCapability();
+ cap.name = fmCapabilities[i];
+ capabilities.frameworkCapabilities[i] = cap;
+ }
+
try {
getRawOutputStream().write(Capabilities.toByteArray(capabilities));
} catch (IOException e) {
@@ -452,10 +471,16 @@
} else {
// Unfortunately we don't have protobuf text format capabilities here.
// Fallback to line separated list instead for text parser.
- pw.println("Format: 1");
+ pw.println("Format: 2");
for (String capability : CAPABILITIES) {
pw.println(capability);
}
+ for (String capability : Debug.getVmFeatureList()) {
+ pw.println("vm:" + capability);
+ }
+ for (String capability : Debug.getFeatureList()) {
+ pw.println("framework:" + capability);
+ }
}
return 0;
}
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 8b1300b..117221f 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -334,6 +334,8 @@
final ActivityManagerService mActivityManagerService;
+ private volatile boolean mLockedBootCompleted = false;
+
static final int TRACKER_TYPE_UNKNOWN = 0;
static final int TRACKER_TYPE_BATTERY = 1;
static final int TRACKER_TYPE_BATTERY_EXEMPTION = 2;
@@ -1721,8 +1723,10 @@
level = RESTRICTION_LEVEL_EXEMPTED;
break;
case STANDBY_BUCKET_NEVER:
- level = RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
- break;
+ if (!android.app.Flags.appRestrictionsApi()) {
+ level = RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
+ break;
+ }
case STANDBY_BUCKET_ACTIVE:
case STANDBY_BUCKET_WORKING_SET:
case STANDBY_BUCKET_FREQUENT:
@@ -1802,7 +1806,9 @@
case STANDBY_BUCKET_EXEMPTED:
return RESTRICTION_LEVEL_EXEMPTED;
case STANDBY_BUCKET_NEVER:
- return RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
+ if (!android.app.Flags.appRestrictionsApi()) {
+ return RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
+ }
case STANDBY_BUCKET_ACTIVE:
case STANDBY_BUCKET_WORKING_SET:
case STANDBY_BUCKET_FREQUENT:
@@ -2153,9 +2159,12 @@
mRestrictionSettings.update(pkgName, uid, level, reason, subReason);
}
- if (!allowUpdateBucket || curBucket == STANDBY_BUCKET_EXEMPTED) {
+ if (!android.app.Flags.appRestrictionsApi()
+ && (!allowUpdateBucket || curBucket == STANDBY_BUCKET_EXEMPTED)) {
return;
}
+
+ boolean doItNow = true;
if (level >= RESTRICTION_LEVEL_RESTRICTED_BUCKET
&& curLevel < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
// Moving the app standby bucket to restricted in the meanwhile.
@@ -2168,7 +2177,6 @@
&& (mConstantsObserver.mBgAutoRestrictedBucket
|| level == RESTRICTION_LEVEL_RESTRICTED_BUCKET)) {
// restrict the app if it hasn't done so.
- boolean doIt = true;
synchronized (mSettingsLock) {
final int index = mActiveUids.indexOfKey(uid, pkgName);
if (index >= 0) {
@@ -2182,14 +2190,16 @@
logAppBackgroundRestrictionInfo(pkgName, uid, curLevel, level,
localTrackerInfo, localReason);
});
- doIt = false;
+ doItNow = false;
}
}
- if (doIt) {
+ if (doItNow) {
appStandbyInternal.restrictApp(pkgName, UserHandle.getUserId(uid),
reason, subReason);
- logAppBackgroundRestrictionInfo(pkgName, uid, curLevel, level, trackerInfo,
- reason);
+ if (!android.app.Flags.appRestrictionsApi()) {
+ logAppBackgroundRestrictionInfo(pkgName, uid, curLevel, level, trackerInfo,
+ reason);
+ }
}
}
} else if (curLevel >= RESTRICTION_LEVEL_RESTRICTED_BUCKET
@@ -2204,6 +2214,14 @@
appStandbyInternal.maybeUnrestrictApp(pkgName, UserHandle.getUserId(uid),
prevReason & REASON_MAIN_MASK, prevReason & REASON_SUB_MASK,
reason, subReason);
+ if (!android.app.Flags.appRestrictionsApi()) {
+ logAppBackgroundRestrictionInfo(pkgName, uid, curLevel, level, trackerInfo,
+ reason);
+ }
+ }
+
+ if (doItNow && android.app.Flags.appRestrictionsApi()
+ && curLevel != RESTRICTION_LEVEL_UNKNOWN) {
logAppBackgroundRestrictionInfo(pkgName, uid, curLevel, level, trackerInfo,
reason);
}
@@ -2297,6 +2315,9 @@
private void handleAppStandbyBucketChanged(int bucket, String packageName,
@UserIdInt int userId) {
+ // Ignore spurious changes to standby bucket during early boot
+ if (android.app.Flags.appRestrictionsApi() && !mLockedBootCompleted) return;
+
final int uid = mInjector.getPackageManagerInternal().getPackageUid(
packageName, STOCK_PM_FLAGS, userId);
final Pair<Integer, TrackerInfo> levelTypePair = calcAppRestrictionLevel(
@@ -3380,6 +3401,7 @@
for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
mAppStateTrackers.get(i).onLockedBootCompleted();
}
+ mLockedBootCompleted = true;
}
boolean isBgAutoRestrictedBucketFeatureFlagEnabled() {
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index ddf1d5f..0728ea8 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -464,7 +464,7 @@
addTimestampToStart(app.info.packageName, app.uid, timeNs, key);
}
- private void addTimestampToStart(String packageName, int uid, long timeNs, int key) {
+ void addTimestampToStart(String packageName, int uid, long timeNs, int key) {
synchronized (mLock) {
AppStartInfoContainer container = mData.get(packageName, uid);
if (container == null) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 4f46ecd..f98799d 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -124,7 +124,9 @@
import com.android.server.power.stats.BatteryStatsDumpHelperImpl;
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.power.stats.BatteryUsageStatsProvider;
-import com.android.server.power.stats.CpuAggregatedPowerStatsProcessor;
+import com.android.server.power.stats.CpuPowerStatsProcessor;
+import com.android.server.power.stats.MobileRadioPowerStatsProcessor;
+import com.android.server.power.stats.PhoneCallPowerStatsProcessor;
import com.android.server.power.stats.PowerStatsAggregator;
import com.android.server.power.stats.PowerStatsExporter;
import com.android.server.power.stats.PowerStatsScheduler;
@@ -408,11 +410,18 @@
com.android.internal.R.bool.config_batteryStatsResetOnUnplugAfterSignificantCharge);
final long powerStatsThrottlePeriodCpu = context.getResources().getInteger(
com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodCpu);
+ final long powerStatsThrottlePeriodMobileRadio = context.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodMobileRadio);
mBatteryStatsConfig =
new BatteryStatsImpl.BatteryStatsConfig.Builder()
.setResetOnUnplugHighBatteryLevel(resetOnUnplugHighBatteryLevel)
.setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge)
- .setPowerStatsThrottlePeriodCpu(powerStatsThrottlePeriodCpu)
+ .setPowerStatsThrottlePeriodMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU,
+ powerStatsThrottlePeriodCpu)
+ .setPowerStatsThrottlePeriodMillis(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ powerStatsThrottlePeriodMobileRadio)
.build();
mPowerStatsUidResolver = new PowerStatsUidResolver();
mStats = new BatteryStatsImpl(mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
@@ -470,7 +479,20 @@
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
.setProcessor(
- new CpuAggregatedPowerStatsProcessor(mPowerProfile, mCpuScalingPolicies));
+ new CpuPowerStatsProcessor(mPowerProfile, mCpuScalingPolicies));
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(
+ new MobileRadioPowerStatsProcessor(mPowerProfile));
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE,
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .setProcessor(new PhoneCallPowerStatsProcessor());
return config;
}
@@ -494,8 +516,16 @@
}
public void systemServicesReady() {
- mStats.setPowerStatsCollectorEnabled(Flags.streamlinedBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(Flags.streamlinedBatteryStats());
+ mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CPU,
+ Flags.streamlinedBatteryStats());
+ mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ Flags.streamlinedConnectivityBatteryStats());
+ mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ BatteryConsumer.POWER_COMPONENT_CPU,
+ Flags.streamlinedBatteryStats());
+ mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ Flags.streamlinedConnectivityBatteryStats());
mWorker.systemServicesReady();
mStats.systemServicesReady(mContext);
mCpuWakeupStats.systemServicesReady();
@@ -536,7 +566,7 @@
* Notifies BatteryStatsService that the system server is ready.
*/
public void onSystemReady() {
- mStats.onSystemReady();
+ mStats.onSystemReady(mContext);
mPowerStatsScheduler.start(Flags.streamlinedBatteryStats());
}
@@ -1591,19 +1621,14 @@
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
mHandler.post(() -> {
- final boolean update;
synchronized (mStats) {
// Ignore if no power state change.
if (mLastPowerStateFromRadio == powerState) return;
mLastPowerStateFromRadio = powerState;
- update = mStats.noteMobileRadioPowerStateLocked(powerState, timestampNs, uid,
+ mStats.noteMobileRadioPowerStateLocked(powerState, timestampNs, uid,
elapsedRealtime, uptime);
}
-
- if (update) {
- mWorker.scheduleSync("modem-data", BatteryExternalStatsWorker.UPDATE_RADIO);
- }
});
}
FrameworkStatsLog.write_non_chained(
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index fb0d695..f336120 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -22,6 +22,8 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_OWNER_CANCELED;
+import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_SUPERSEDED;
import android.annotation.Nullable;
import android.app.Activity;
@@ -54,6 +56,7 @@
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AlarmManagerInternal;
import com.android.server.LocalServices;
+import com.android.server.am.PendingIntentRecord.CancellationReason;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.SafeActivityOptions;
@@ -191,7 +194,7 @@
}
return rec;
}
- makeIntentSenderCanceled(rec);
+ makeIntentSenderCanceled(rec, CANCEL_REASON_SUPERSEDED);
mIntentSenderRecords.remove(key);
decrementUidStatLocked(rec);
}
@@ -206,7 +209,7 @@
}
boolean removePendingIntentsForPackage(String packageName, int userId, int appId,
- boolean doIt) {
+ boolean doIt, @CancellationReason int cancelReason) {
boolean didSomething = false;
synchronized (mLock) {
@@ -256,7 +259,7 @@
}
didSomething = true;
it.remove();
- makeIntentSenderCanceled(pir);
+ makeIntentSenderCanceled(pir, cancelReason);
decrementUidStatLocked(pir);
if (pir.key.activity != null) {
final Message m = PooledLambda.obtainMessage(
@@ -289,13 +292,14 @@
} catch (RemoteException e) {
throw new SecurityException(e);
}
- cancelIntentSender(rec, true);
+ cancelIntentSender(rec, true, CANCEL_REASON_OWNER_CANCELED);
}
}
- public void cancelIntentSender(PendingIntentRecord rec, boolean cleanActivity) {
+ public void cancelIntentSender(PendingIntentRecord rec, boolean cleanActivity,
+ @CancellationReason int cancelReason) {
synchronized (mLock) {
- makeIntentSenderCanceled(rec);
+ makeIntentSenderCanceled(rec, cancelReason);
mIntentSenderRecords.remove(rec.key);
decrementUidStatLocked(rec);
if (cleanActivity && rec.key.activity != null) {
@@ -359,8 +363,10 @@
}
}
- private void makeIntentSenderCanceled(PendingIntentRecord rec) {
+ private void makeIntentSenderCanceled(PendingIntentRecord rec,
+ @CancellationReason int cancelReason) {
rec.canceled = true;
+ rec.cancelReason = cancelReason;
final RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked();
if (callbacks != null) {
final Message m = PooledLambda.obtainMessage(
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 95e130e..da45a77 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -16,11 +16,13 @@
package com.android.server.am;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.START_SUCCESS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
@@ -51,11 +53,15 @@
import android.util.Slog;
import android.util.TimeUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.modules.expresslog.Counter;
import com.android.server.wm.SafeActivityOptions;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.Objects;
@@ -71,12 +77,35 @@
public static final int FLAG_BROADCAST_SENDER = 1 << 1;
public static final int FLAG_SERVICE_SENDER = 1 << 2;
+ public static final int CANCEL_REASON_NULL = 0;
+ public static final int CANCEL_REASON_USER_STOPPED = 1 << 0;
+ public static final int CANCEL_REASON_OWNER_UNINSTALLED = 1 << 1;
+ public static final int CANCEL_REASON_OWNER_FORCE_STOPPED = 1 << 2;
+ public static final int CANCEL_REASON_OWNER_CANCELED = 1 << 3;
+ public static final int CANCEL_REASON_HOSTING_ACTIVITY_DESTROYED = 1 << 4;
+ public static final int CANCEL_REASON_SUPERSEDED = 1 << 5;
+ public static final int CANCEL_REASON_ONE_SHOT_SENT = 1 << 6;
+
+ @IntDef({
+ CANCEL_REASON_NULL,
+ CANCEL_REASON_USER_STOPPED,
+ CANCEL_REASON_OWNER_UNINSTALLED,
+ CANCEL_REASON_OWNER_FORCE_STOPPED,
+ CANCEL_REASON_OWNER_CANCELED,
+ CANCEL_REASON_HOSTING_ACTIVITY_DESTROYED,
+ CANCEL_REASON_SUPERSEDED,
+ CANCEL_REASON_ONE_SHOT_SENT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CancellationReason {}
+
final PendingIntentController controller;
final Key key;
final int uid;
public final WeakReference<PendingIntentRecord> ref;
boolean sent = false;
boolean canceled = false;
+ @CancellationReason int cancelReason = CANCEL_REASON_NULL;
/**
* Map IBinder to duration specified as Pair<Long, Integer>, Long is allowlist duration in
* milliseconds, Integer is allowlist type defined at
@@ -419,12 +448,22 @@
SafeActivityOptions mergedOptions = null;
synchronized (controller.mLock) {
if (canceled) {
+ if (cancelReason == CANCEL_REASON_OWNER_FORCE_STOPPED
+ && controller.mAmInternal.getUidProcessState(callingUid)
+ == PROCESS_STATE_TOP) {
+ Counter.logIncrementWithUid(
+ "app.value_force_stop_cancelled_pi_sent_from_top_per_caller",
+ callingUid);
+ Counter.logIncrementWithUid(
+ "app.value_force_stop_cancelled_pi_sent_from_top_per_owner",
+ uid);
+ }
return ActivityManager.START_CANCELED;
}
sent = true;
if ((key.flags & PendingIntent.FLAG_ONE_SHOT) != 0) {
- controller.cancelIntentSender(this, true);
+ controller.cancelIntentSender(this, true, CANCEL_REASON_ONE_SHOT_SENT);
}
finalIntent = key.requestIntent != null ? new Intent(key.requestIntent) : new Intent();
@@ -687,6 +726,21 @@
}
}
+ @VisibleForTesting
+ static String cancelReasonToString(@CancellationReason int cancelReason) {
+ return switch (cancelReason) {
+ case CANCEL_REASON_NULL -> "NULL";
+ case CANCEL_REASON_USER_STOPPED -> "USER_STOPPED";
+ case CANCEL_REASON_OWNER_UNINSTALLED -> "OWNER_UNINSTALLED";
+ case CANCEL_REASON_OWNER_FORCE_STOPPED -> "OWNER_FORCE_STOPPED";
+ case CANCEL_REASON_OWNER_CANCELED -> "OWNER_CANCELED";
+ case CANCEL_REASON_HOSTING_ACTIVITY_DESTROYED -> "HOSTING_ACTIVITY_DESTROYED";
+ case CANCEL_REASON_SUPERSEDED -> "SUPERSEDED";
+ case CANCEL_REASON_ONE_SHOT_SENT -> "ONE_SHOT_SENT";
+ default -> "UNKNOWN";
+ };
+ }
+
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("uid="); pw.print(uid);
pw.print(" packageName="); pw.print(key.packageName);
@@ -707,7 +761,8 @@
}
if (sent || canceled) {
pw.print(prefix); pw.print("sent="); pw.print(sent);
- pw.print(" canceled="); pw.println(canceled);
+ pw.print(" canceled="); pw.print(canceled);
+ pw.print(" cancelReason="); pw.println(cancelReasonToString(cancelReason));
}
if (mAllowlistDuration != null) {
pw.print(prefix);
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 0aa1a69..76c5952 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -66,7 +66,7 @@
import java.io.StringWriter;
import java.time.Instant;
import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
+import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
@@ -79,9 +79,6 @@
* The error state of the process, such as if it's crashing/ANR etc.
*/
class ProcessErrorStateRecord {
- private static final DateTimeFormatter DROPBOX_TIME_FORMATTER =
- DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSZ");
-
final ProcessRecord mApp;
private final ActivityManagerService mService;
@@ -355,9 +352,18 @@
synchronized (mProcLock) {
latencyTracker.waitingOnProcLockEnded();
setNotResponding(true);
+
+ ZonedDateTime timestamp = null;
+ if (timeoutRecord != null && timeoutRecord.mEndUptimeMillis > 0) {
+ long millisSinceEndUptimeMs = anrTime - timeoutRecord.mEndUptimeMillis;
+ timestamp = Instant.now().minusMillis(millisSinceEndUptimeMs)
+ .atZone(ZoneId.systemDefault());
+ }
+
volatileDropboxEntriyStates =
ActivityManagerService.VolatileDropboxEntryStates
- .withProcessFrozenState(mApp.mOptRecord.isFrozen());
+ .withProcessFrozenStateAndTimestamp(
+ mApp.mOptRecord.isFrozen(), timestamp);
}
// Log the ANR to the event log.
@@ -450,13 +456,6 @@
info.append("ErrorId: ").append(errorId.toString()).append("\n");
}
info.append("Frozen: ").append(mApp.mOptRecord.isFrozen()).append("\n");
- if (timeoutRecord != null && timeoutRecord.mEndUptimeMillis > 0) {
- long millisSinceEndUptimeMs = anrTime - timeoutRecord.mEndUptimeMillis;
- String formattedTime = DROPBOX_TIME_FORMATTER.format(
- Instant.now().minusMillis(millisSinceEndUptimeMs)
- .atZone(ZoneId.systemDefault()));
- info.append("Timestamp: ").append(formattedTime).append("\n");
- }
// Retrieve controller with max ANR delay from AnrControllers
// Note that we retrieve the controller before dumping stacks because dumping stacks can
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 390dca3..1f89ca7 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -145,6 +145,7 @@
"core_experiments_team_internal",
"core_graphics",
"core_libraries",
+ "crumpet",
"dck_framework",
"devoptions_settings",
"game",
@@ -169,6 +170,7 @@
"pixel_biometrics_face",
"pixel_bluetooth",
"pixel_connectivity_gps",
+ "pixel_continuity",
"pixel_sensors",
"pixel_system_sw_video",
"pixel_watch",
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index debd9d0..a1f80d0 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -70,6 +70,7 @@
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
+import static android.permission.flags.Flags.runtimePermissionAppopsMappingEnabled;
import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
@@ -248,6 +249,7 @@
Process.ROOT_UID,
Process.PHONE_UID,
Process.BLUETOOTH_UID,
+ Process.AUDIOSERVER_UID,
Process.NFC_UID,
Process.NETWORK_STACK_UID,
Process.SHELL_UID};
@@ -1364,6 +1366,9 @@
@GuardedBy("this")
private void packageRemovedLocked(int uid, String packageName) {
+ mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::clearHistory,
+ mHistoricalRegistry, uid, packageName));
+
UidState uidState = mUidStates.get(uid);
if (uidState == null) {
return;
@@ -1398,9 +1403,6 @@
}
}
}
-
- mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::clearHistory,
- mHistoricalRegistry, uid, packageName));
}
public void uidRemoved(int uid) {
@@ -2682,6 +2684,15 @@
}
}
+ /**
+ * When querying the mode these should always be allowed and the checking service might not
+ * have information on them.
+ */
+ private static boolean isOpAllowedForUid(int uid) {
+ return runtimePermissionAppopsMappingEnabled()
+ && (uid == Process.ROOT_UID || uid == Process.SYSTEM_UID);
+ }
+
@Override
public int checkOperationRaw(int code, int uid, String packageName,
@Nullable String attributionTag) {
@@ -2757,6 +2768,9 @@
pvr.bypass, true)) {
return AppOpsManager.MODE_IGNORED;
}
+ if (isOpAllowedForUid(uid)) {
+ return MODE_ALLOWED;
+ }
code = AppOpsManager.opToSwitch(code);
UidState uidState = getUidStateLocked(uid, false);
if (uidState != null
@@ -3071,9 +3085,12 @@
return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
packageName);
}
- // If there is a non-default per UID policy (we set UID op mode only if
- // non-default) it takes over, otherwise use the per package policy.
- if (mAppOpsCheckingService.getUidMode(
+ if (isOpAllowedForUid(uid)) {
+ // Op is always allowed for the UID, do nothing.
+
+ // If there is a non-default per UID policy (we set UID op mode only if
+ // non-default) it takes over, otherwise use the per package policy.
+ } else if (mAppOpsCheckingService.getUidMode(
uidState.uid, getPersistentId(virtualDeviceId), switchCode)
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode =
@@ -3665,10 +3682,13 @@
isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag,
virtualDeviceId, pvr.bypass, false);
final int switchCode = AppOpsManager.opToSwitch(code);
- // If there is a non-default per UID policy (we set UID op mode only if
- // non-default) it takes over, otherwise use the per package policy.
- if (mAppOpsCheckingService.getUidMode(
- uidState.uid, getPersistentId(virtualDeviceId), switchCode)
+ if (isOpAllowedForUid(uid)) {
+ // Op is always allowed for the UID, do nothing.
+
+ // If there is a non-default per UID policy (we set UID op mode only if
+ // non-default) it takes over, otherwise use the per package policy.
+ } else if (mAppOpsCheckingService.getUidMode(
+ uidState.uid, getPersistentId(virtualDeviceId), switchCode)
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode =
uidState.evalMode(
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 6f3526f..dbd47d0 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -54,13 +54,13 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.AtomicDirectory;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.FgThread;
+import com.android.server.IoThread;
import org.xmlpull.v1.XmlPullParserException;
@@ -669,7 +669,7 @@
}
private void clearHistoryOnDiskDLocked() {
- BackgroundThread.getHandler().removeMessages(MSG_WRITE_PENDING_HISTORY);
+ IoThread.getHandler().removeMessages(MSG_WRITE_PENDING_HISTORY);
synchronized (mInMemoryLock) {
mCurrentHistoricalOps = null;
mNextPersistDueTimeMillis = System.currentTimeMillis();
@@ -745,7 +745,7 @@
private void persistPendingHistory(@NonNull List<HistoricalOps> pendingWrites) {
synchronized (mOnDiskLock) {
- BackgroundThread.getHandler().removeMessages(MSG_WRITE_PENDING_HISTORY);
+ IoThread.getHandler().removeMessages(MSG_WRITE_PENDING_HISTORY);
if (pendingWrites.isEmpty()) {
return;
}
@@ -767,7 +767,7 @@
final Message message = PooledLambda.obtainMessage(
HistoricalRegistry::persistPendingHistory, HistoricalRegistry.this);
message.what = MSG_WRITE_PENDING_HISTORY;
- BackgroundThread.getHandler().sendMessage(message);
+ IoThread.getHandler().sendMessage(message);
mPendingWrites.offerFirst(ops);
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index f38b381..9bdc51e 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -987,9 +987,9 @@
}
if (di.mPeerDeviceAddress.equals("")) {
for (Pair<String, String> addr : addresses) {
- if (!addr.first.equals(di.mDeviceAddress)) {
- di.mPeerDeviceAddress = addr.first;
- di.mPeerIdentityDeviceAddress = addr.second;
+ if (!di.mDeviceAddress.equals(addr.first)) {
+ di.mPeerDeviceAddress = TextUtils.emptyIfNull(addr.first);
+ di.mPeerIdentityDeviceAddress = TextUtils.emptyIfNull(addr.second);
break;
}
}
@@ -1000,8 +1000,8 @@
}
if (di.mDeviceIdentityAddress.equals("")) {
for (Pair<String, String> addr : addresses) {
- if (addr.first.equals(di.mDeviceAddress)) {
- di.mDeviceIdentityAddress = addr.second;
+ if (di.mDeviceAddress.equals(addr.first)) {
+ di.mDeviceIdentityAddress = TextUtils.emptyIfNull(addr.second);
break;
}
}
diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
index 570d4e9..e330ed5 100644
--- a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
+++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
@@ -16,6 +16,11 @@
package com.android.server.audio;
+import static android.media.AudioManager.ADJUST_LOWER;
+import static android.media.AudioManager.ADJUST_MUTE;
+import static android.media.AudioManager.ADJUST_RAISE;
+import static android.media.AudioManager.ADJUST_UNMUTE;
+
import android.content.Context;
import android.media.AudioManager;
import android.os.ShellCommand;
@@ -59,6 +64,12 @@
return adjMute();
case "adj-unmute":
return adjUnmute();
+ case "adj-volume":
+ return adjVolume();
+ case "set-group-volume":
+ return setGroupVolume();
+ case "adj-group-volume":
+ return adjGroupVolume();
}
return 0;
}
@@ -90,6 +101,12 @@
pw.println(" mutes the STREAM_TYPE");
pw.println(" adj-unmute STREAM_TYPE");
pw.println(" unmutes the STREAM_TYPE");
+ pw.println(" adj-volume STREAM_TYPE <RAISE|LOWER|MUTE|UNMUTE>");
+ pw.println(" Adjusts the STREAM_TYPE volume given the specified direction");
+ pw.println(" set-group-volume GROUP_ID VOLUME_INDEX");
+ pw.println(" Sets the volume for GROUP_ID to VOLUME_INDEX");
+ pw.println(" adj-group-volume GROUP_ID <RAISE|LOWER|MUTE|UNMUTE>");
+ pw.println(" Adjusts the group volume for GROUP_ID given the specified direction");
}
private int setSurroundFormatEnabled() {
@@ -260,15 +277,48 @@
return 0;
}
+ private int adjVolume() {
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ final int stream = readIntArg();
+ final int direction = readDirectionArg();
+ getOutPrintWriter().println("calling AudioManager.adjustStreamVolume("
+ + stream + ", " + direction + ", 0)");
+ am.adjustStreamVolume(stream, direction, 0);
+ return 0;
+ }
+
+ private int setGroupVolume() {
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ final int groupId = readIntArg();
+ final int index = readIntArg();
+ getOutPrintWriter().println("calling AudioManager.setVolumeGroupVolumeIndex("
+ + groupId + ", " + index + ", 0)");
+ am.setVolumeGroupVolumeIndex(groupId, index, 0);
+ return 0;
+ }
+
+ private int adjGroupVolume() {
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ final int groupId = readIntArg();
+ final int direction = readDirectionArg();
+ getOutPrintWriter().println("calling AudioManager.adjustVolumeGroupVolume("
+ + groupId + ", " + direction + ", 0)");
+ am.adjustVolumeGroupVolume(groupId, direction, 0);
+ return 0;
+ }
+
private int readIntArg() throws IllegalArgumentException {
- String argText = getNextArg();
+ final String argText = getNextArg();
if (argText == null) {
getErrPrintWriter().println("Error: no argument provided");
throw new IllegalArgumentException("No argument provided");
}
- int argIntVal = Integer.MIN_VALUE;
+ int argIntVal;
try {
argIntVal = Integer.parseInt(argText);
} catch (NumberFormatException e) {
@@ -278,4 +328,21 @@
return argIntVal;
}
+
+ private int readDirectionArg() throws IllegalArgumentException {
+ final String argText = getNextArg();
+
+ if (argText == null) {
+ getErrPrintWriter().println("Error: no argument provided");
+ throw new IllegalArgumentException("No argument provided");
+ }
+
+ return switch (argText) {
+ case "RAISE" -> ADJUST_RAISE;
+ case "LOWER" -> ADJUST_LOWER;
+ case "MUTE" -> ADJUST_MUTE;
+ case "UNMUTE" -> ADJUST_UNMUTE;
+ default -> throw new IllegalArgumentException("Wrong direction argument: " + argText);
+ };
+ }
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5ba0af4..c11fbe1 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4674,6 +4674,12 @@
int streamTypeAlias = mStreamVolumeAlias[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
+ if ((streamType == AudioManager.STREAM_VOICE_CALL)
+ && isInCommunication() && mDeviceBroker.isBluetoothScoActive()) {
+ Log.i(TAG, "setStreamVolume for STREAM_VOICE_CALL, switching to STREAM_BLUETOOTH_SCO");
+ streamType = AudioManager.STREAM_BLUETOOTH_SCO;
+ }
+
final int device = (ada == null)
? getDeviceForStream(streamType)
: ada.getInternalType();
@@ -5464,19 +5470,19 @@
/** @see AudioManager#setMicrophoneMuteFromSwitch(boolean) */
public void setMicrophoneMuteFromSwitch(boolean on) {
- int userId = Binder.getCallingUid();
- if (userId != android.os.Process.SYSTEM_UID) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != android.os.Process.SYSTEM_UID) {
Log.e(TAG, "setMicrophoneMuteFromSwitch() called from non system user!");
return;
}
mMicMuteFromSwitch = on;
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIC)
- .setUid(userId)
+ .setUid(callingUid)
.set(MediaMetrics.Property.EVENT, "setMicrophoneMuteFromSwitch")
.set(MediaMetrics.Property.REQUEST, on
? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE)
.record();
- setMicrophoneMuteNoCallerCheck(userId);
+ setMicrophoneMuteNoCallerCheck(UserHandle.getCallingUserId());
}
private void setMicMuteFromSwitchInput() {
@@ -5507,9 +5513,10 @@
if (DEBUG_VOL) {
Log.d(TAG, String.format("Mic mute %b, user=%d", muted, userId));
}
- // only mute for the current user
- if (getCurrentUserId() == userId || userId == android.os.Process.SYSTEM_UID) {
+ // only mute for the current user or for the system user.
+ if (getCurrentUserId() == userId || userId == UserHandle.USER_SYSTEM) {
final boolean currentMute = mAudioSystem.isMicrophoneMuted();
+ int callingUid = Binder.getCallingUid();
final long identity = Binder.clearCallingIdentity();
try {
final int ret = mAudioSystem.muteMicrophone(muted);
@@ -5522,7 +5529,7 @@
}
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIC)
- .setUid(userId)
+ .setUid(callingUid)
.set(MediaMetrics.Property.EVENT, "setMicrophoneMuteNoCallerCheck")
.set(MediaMetrics.Property.MUTE, mMicMuteFromSystemCached
? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
@@ -7903,6 +7910,7 @@
DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_LINE);
+ DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_HEARING_AID);
DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET);
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index edeabdc..a649d34 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -1110,6 +1110,12 @@
return mLeAudio.getGroupId(device);
}
+ /**
+ * Returns all addresses and identity addresses for LE Audio devices a group.
+ * @param groupId The ID of the group from which to get addresses.
+ * @return A List of Pair(String main_address, String identity_address). Note that the
+ * addresses returned by BluetoothDevice can be null.
+ */
/*package*/ List<Pair<String, String>> getLeAudioGroupAddresses(int groupId) {
List<Pair<String, String>> addresses = new ArrayList<>();
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
diff --git a/services/core/java/com/android/server/audio/MusicFxHelper.java b/services/core/java/com/android/server/audio/MusicFxHelper.java
index 85b3b49..ba45310 100644
--- a/services/core/java/com/android/server/audio/MusicFxHelper.java
+++ b/services/core/java/com/android/server/audio/MusicFxHelper.java
@@ -90,7 +90,6 @@
* observer will also be removed, and observer token reset to null
*/
private class MySparseArray extends SparseArray<PackageSessions> {
- private final String mMusicFxPackageName = "com.android.musicfx";
@RequiresPermission(anyOf = {
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -229,6 +228,10 @@
if (ril != null && ril.size() != 0) {
ResolveInfo ri = ril.get(0);
final String senderPackageName = intent.getStringExtra(AudioEffect.EXTRA_PACKAGE_NAME);
+ if (senderPackageName == null) {
+ Log.w(TAG, "Intent package name must not be null");
+ return;
+ }
try {
if (ri != null && ri.activityInfo != null && ri.activityInfo.packageName != null) {
final int senderUid = pm.getPackageUidAsUser(senderPackageName,
@@ -265,7 +268,7 @@
+ senderUid + ", package: " + senderPackageName + ", abort");
return false;
}
- if (pkgSessions.mPackageName != senderPackageName) {
+ if (!pkgSessions.mPackageName.equals(senderPackageName)) {
Log.w(TAG, "Inconsistency package names for UID open: " + senderUid + " prev: "
+ pkgSessions.mPackageName + ", now: " + senderPackageName);
return false;
@@ -297,7 +300,7 @@
Log.e(TAG, senderPackageName + " UID " + senderUid + " does not exist in map, abort");
return false;
}
- if (pkgSessions.mPackageName != senderPackageName) {
+ if (!pkgSessions.mPackageName.equals(senderPackageName)) {
Log.w(TAG, "Inconsistency package names for UID " + senderUid + " close, prev: "
+ pkgSessions.mPackageName + ", now: " + senderPackageName);
return false;
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 7df63b1..11cca66 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -25,15 +25,12 @@
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -778,13 +775,7 @@
hidlConfigs = null;
}
- if (com.android.server.biometrics.Flags.deHidl()) {
- registerAuthenticators();
- } else {
- // Registers HIDL and AIDL authenticators, but only HIDL configs need to be provided.
- registerAuthenticators(hidlConfigs);
- }
-
+ registerAuthenticators();
mInjector.publishBinderService(this, mImpl);
}
@@ -874,7 +865,7 @@
if (faceService != null) {
try {
- faceService.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ faceService.registerAuthenticators(mFaceSensorConfigurations);
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException when registering face authenticators", e);
}
@@ -912,8 +903,7 @@
if (fingerprintService != null) {
try {
- fingerprintService.registerAuthenticatorsLegacy(
- mFingerprintSensorConfigurations);
+ fingerprintService.registerAuthenticators(mFingerprintSensorConfigurations);
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException when registering fingerprint authenticators", e);
}
@@ -948,78 +938,6 @@
return configStrings;
}
- /**
- * Registers HIDL and AIDL authenticators for all of the available modalities.
- *
- * @param hidlSensors Array of {@link SensorConfig} configuration for all of the HIDL sensors
- * available on the device. This array may contain configuration for
- * different modalities and different sensors of the same modality in
- * arbitrary order. Can be null if no HIDL sensors exist on the device.
- */
- private void registerAuthenticators(@Nullable SensorConfig[] hidlSensors) {
- List<FingerprintSensorPropertiesInternal> hidlFingerprintSensors = new ArrayList<>();
- List<FaceSensorPropertiesInternal> hidlFaceSensors = new ArrayList<>();
- // Iris doesn't have IrisSensorPropertiesInternal, using SensorPropertiesInternal instead.
- List<SensorPropertiesInternal> hidlIrisSensors = new ArrayList<>();
-
- if (hidlSensors != null) {
- for (SensorConfig sensor : hidlSensors) {
- Slog.d(TAG, "Registering HIDL ID: " + sensor.id + " Modality: " + sensor.modality
- + " Strength: " + sensor.strength);
- switch (sensor.modality) {
- case TYPE_FINGERPRINT:
- hidlFingerprintSensors.add(
- getHidlFingerprintSensorProps(sensor.id, sensor.strength));
- break;
-
- case TYPE_FACE:
- hidlFaceSensors.add(getHidlFaceSensorProps(sensor.id, sensor.strength));
- break;
-
- case TYPE_IRIS:
- hidlIrisSensors.add(getHidlIrisSensorProps(sensor.id, sensor.strength));
- break;
-
- default:
- Slog.e(TAG, "Unknown modality: " + sensor.modality);
- }
- }
- }
-
- final IFingerprintService fingerprintService = mInjector.getFingerprintService();
- if (fingerprintService != null) {
- try {
- fingerprintService.registerAuthenticators(hidlFingerprintSensors);
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException when registering fingerprint authenticators", e);
- }
- } else if (hidlFingerprintSensors.size() > 0) {
- Slog.e(TAG, "HIDL fingerprint configuration exists, but FingerprintService is null.");
- }
-
- final IFaceService faceService = mInjector.getFaceService();
- if (faceService != null) {
- try {
- faceService.registerAuthenticators(hidlFaceSensors);
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException when registering face authenticators", e);
- }
- } else if (hidlFaceSensors.size() > 0) {
- Slog.e(TAG, "HIDL face configuration exists, but FaceService is null.");
- }
-
- final IIrisService irisService = mInjector.getIrisService();
- if (irisService != null) {
- try {
- irisService.registerAuthenticators(hidlIrisSensors);
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException when registering iris authenticators", e);
- }
- } else if (hidlIrisSensors.size() > 0) {
- Slog.e(TAG, "HIDL iris configuration exists, but IrisService is null.");
- }
- }
-
private void checkInternalPermission() {
getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
"Must have USE_BIOMETRIC_INTERNAL permission");
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index c03b3b8..0be893c 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -576,7 +576,8 @@
}
void onDialogAnimatedIn(boolean startFingerprintNow) {
- if (mState != STATE_AUTH_STARTED && mState != STATE_ERROR_PENDING_SYSUI) {
+ if (mState != STATE_AUTH_STARTED && mState != STATE_ERROR_PENDING_SYSUI
+ && mState != STATE_AUTH_PAUSED) {
Slog.e(TAG, "onDialogAnimatedIn, unexpected state: " + mState);
return;
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricHandlerProvider.java b/services/core/java/com/android/server/biometrics/BiometricHandlerProvider.java
index e578861..91cabb5 100644
--- a/services/core/java/com/android/server/biometrics/BiometricHandlerProvider.java
+++ b/services/core/java/com/android/server/biometrics/BiometricHandlerProvider.java
@@ -21,7 +21,6 @@
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Looper;
/**
* This class provides the handler to process biometric operations.
@@ -76,11 +75,8 @@
}
private Handler getNewHandler(String tag, int priority) {
- if (Flags.deHidl()) {
- HandlerThread handlerThread = new HandlerThread(tag, priority);
- handlerThread.start();
- return new Handler(handlerThread.getLooper());
- }
- return new Handler(Looper.getMainLooper());
+ HandlerThread handlerThread = new HandlerThread(tag, priority);
+ handlerThread.start();
+ return new Handler(handlerThread.getLooper());
}
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContext.java b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
index 7f04628..7a8e25b 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContext.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
@@ -81,22 +81,6 @@
boolean isHardwareIgnoringTouches();
/**
- * Subscribe to context changes.
- *
- * Note that this method only notifies for properties that are visible to the HAL.
- *
- * @param context context that will be modified when changed
- * @param consumer callback when the context is modified
- *
- * @deprecated instead use {@link BiometricContext#subscribe(OperationContextExt, Consumer,
- * Consumer, AuthenticateOptions)}
- * TODO (b/294161627): Delete this API once Flags.DE_HIDL is removed.
- */
- @Deprecated
- void subscribe(@NonNull OperationContextExt context,
- @NonNull Consumer<OperationContext> consumer);
-
- /**
* Subscribe to context changes and start the HAL operation.
*
* Note that this method only notifies for properties that are visible to the HAL.
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
index d8dfa60..a17de3d 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
@@ -229,16 +229,6 @@
@Override
public void subscribe(@NonNull OperationContextExt context,
- @NonNull Consumer<OperationContext> consumer) {
- mSubscribers.put(context, consumer);
- // TODO(b/294161627) Combine the getContext/subscribe APIs to avoid race
- if (context.getDisplayState() != getDisplayState()) {
- consumer.accept(context.update(this, context.isCrypto()).toAidlContext());
- }
- }
-
- @Override
- public void subscribe(@NonNull OperationContextExt context,
@NonNull Consumer<OperationContext> startHalConsumer,
@NonNull Consumer<OperationContext> updateContextConsumer,
@Nullable AuthenticateOptions options) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 4fa8741..1e2451c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -35,7 +35,6 @@
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -117,24 +116,20 @@
@LockoutTracker.LockoutMode
public int handleFailedAttempt(int userId) {
- if (Flags.deHidl()) {
- if (mLockoutTracker != null) {
- mLockoutTracker.addFailedAttemptForUser(getTargetUserId());
- }
- @LockoutTracker.LockoutMode final int lockoutMode =
- getLockoutTracker().getLockoutModeForUser(userId);
- final PerformanceTracker performanceTracker =
- PerformanceTracker.getInstanceForSensorId(getSensorId());
- if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
- performanceTracker.incrementPermanentLockoutForUser(userId);
- } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
- performanceTracker.incrementTimedLockoutForUser(userId);
- }
-
- return lockoutMode;
- } else {
- return LockoutTracker.LOCKOUT_NONE;
+ if (mLockoutTracker != null) {
+ mLockoutTracker.addFailedAttemptForUser(getTargetUserId());
}
+ @LockoutTracker.LockoutMode final int lockoutMode =
+ getLockoutTracker().getLockoutModeForUser(userId);
+ final PerformanceTracker performanceTracker =
+ PerformanceTracker.getInstanceForSensorId(getSensorId());
+ if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
+ performanceTracker.incrementPermanentLockoutForUser(userId);
+ } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
+ performanceTracker.incrementTimedLockoutForUser(userId);
+ }
+
+ return lockoutMode;
}
protected long getStartTimeMs() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 89e08c1..82d5d4d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -19,9 +19,9 @@
import static com.android.server.biometrics.sensors.BiometricSchedulerOperation.STATE_STARTED;
import android.annotation.IntDef;
-import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.WorkerThread;
import android.content.Context;
import android.hardware.biometrics.IBiometricService;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -38,7 +38,6 @@
import com.android.modules.expresslog.Counter;
import com.android.server.biometrics.BiometricSchedulerProto;
import com.android.server.biometrics.BiometricsProto;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import java.io.PrintWriter;
@@ -65,9 +64,8 @@
* @param <T> Hal instance for starting the user.
* @param <U> Session associated with the current user id.
*
- * TODO: (b/304604965) Update thread annotation when FLAGS_DE_HIDL is removed.
*/
-@MainThread
+@WorkerThread
public class BiometricScheduler<T, U> {
private static final String TAG = "BiometricScheduler";
@@ -176,7 +174,7 @@
Slog.w(TAG, "operation is already null or different (reset?): "
+ mCurrentOperation);
}
- startNextOperationIfIdle();
+ checkCurrentUserAndStartNextOperation();
});
}
}
@@ -219,7 +217,7 @@
mRecentOperations.add(mCurrentOperation.getProtoEnum());
mCurrentOperation = null;
mTotalOperationsHandled++;
- startNextOperationIfIdle();
+ checkCurrentUserAndStartNextOperation();
});
}
};
@@ -304,15 +302,7 @@
return mInternalCallback;
}
- protected void startNextOperationIfIdle() {
- if (Flags.deHidl()) {
- startNextOperation();
- } else {
- startNextOperationIfIdleLegacy();
- }
- }
-
- protected void startNextOperation() {
+ protected void checkCurrentUserAndStartNextOperation() {
if (mCurrentOperation != null) {
Slog.v(TAG, "Not idle, current operation: " + mCurrentOperation);
return;
@@ -326,7 +316,7 @@
final int nextUserId = mPendingOperations.getFirst().getTargetUserId();
if (nextUserId == currentUserId || mPendingOperations.getFirst().isStartUserOperation()) {
- startNextOperationIfIdleLegacy();
+ startNextOperationIfIdle();
} else if (currentUserId == UserHandle.USER_NULL && mUserSwitchProvider != null) {
final BaseClientMonitor startClient =
mUserSwitchProvider.getStartUserClient(nextUserId);
@@ -357,7 +347,7 @@
}
}
- protected void startNextOperationIfIdleLegacy() {
+ protected void startNextOperationIfIdle() {
if (mCurrentOperation != null) {
Slog.v(TAG, "Not idle, current operation: " + mCurrentOperation);
return;
@@ -422,7 +412,7 @@
// run these. A single request from the manager layer to the service layer may
// actually be multiple operations (i.e. updateActiveUser + authenticate).
mCurrentOperation = null;
- startNextOperationIfIdle();
+ checkCurrentUserAndStartNextOperation();
}
} else {
try {
@@ -459,7 +449,7 @@
} else {
Slog.e(TAG, "[Unable To Start] Prepared client: " + mCurrentOperation);
mCurrentOperation = null;
- startNextOperationIfIdle();
+ checkCurrentUserAndStartNextOperation();
}
}
@@ -504,7 +494,7 @@
Slog.d(TAG, "[Cancelling Interruptable]: " + mCurrentOperation);
mCurrentOperation.cancel(mHandler, mInternalCallback);
} else {
- startNextOperationIfIdle();
+ checkCurrentUserAndStartNextOperation();
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
deleted file mode 100644
index 7ca10e3..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2021 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.biometrics.sensors;
-
-import static com.android.server.biometrics.sensors.BiometricSchedulerOperation.STATE_STARTED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.hardware.biometrics.IBiometricService;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
-
-/**
- * A user-aware scheduler that requests user-switches based on scheduled operation's targetUserId.
- * TODO (b/304604965): Remove class when Flags.FLAG_DE_HIDL is removed.
- *
- * @param <T> Hal instance for starting the user.
- * @param <U> Session associated with the current user id.
- */
-public class UserAwareBiometricScheduler<T, U> extends BiometricScheduler<T, U> {
-
- private static final String TAG = "UaBiometricScheduler";
-
- /**
- * Interface to retrieve the owner's notion of the current userId. Note that even though
- * the scheduler can determine this based on its history of processed clients, we should still
- * query the owner since it may be cleared due to things like HAL death, etc.
- */
- public interface CurrentUserRetriever {
- int getCurrentUserId();
- }
-
- public interface UserSwitchCallback {
- @NonNull StopUserClient<?> getStopUserClient(int userId);
- @NonNull StartUserClient<?, ?> getStartUserClient(int newUserId);
- }
-
- @NonNull private final CurrentUserRetriever mCurrentUserRetriever;
- @NonNull private final UserSwitchCallback mUserSwitchCallback;
- @Nullable private StopUserClient<?> mStopUserClient;
-
- private class ClientFinishedCallback implements ClientMonitorCallback {
- @NonNull private final BaseClientMonitor mOwner;
-
- ClientFinishedCallback(@NonNull BaseClientMonitor owner) {
- mOwner = owner;
- }
-
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
- mHandler.post(() -> {
- Slog.d(TAG, "[Client finished] " + clientMonitor + ", success: " + success);
-
- // Set mStopUserClient to null when StopUserClient fails. Otherwise it's possible
- // for that the queue will wait indefinitely until the field is cleared.
- if (clientMonitor instanceof StopUserClient<?>) {
- if (!success) {
- Slog.w(TAG, "StopUserClient failed(), is the HAL stuck? "
- + "Clearing mStopUserClient");
- }
- mStopUserClient = null;
- }
- if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) {
- mCurrentOperation = null;
- } else {
- // can happen if the hal dies and is usually okay
- // do not unset the current operation that may be newer
- Slog.w(TAG, "operation is already null or different (reset?): "
- + mCurrentOperation);
- }
- startNextOperationIfIdle();
- });
- }
- }
-
- @VisibleForTesting
- public UserAwareBiometricScheduler(@NonNull String tag,
- @NonNull Handler handler,
- @SensorType int sensorType,
- @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull IBiometricService biometricService,
- @NonNull CurrentUserRetriever currentUserRetriever,
- @NonNull UserSwitchCallback userSwitchCallback) {
- super(handler, sensorType, gestureAvailabilityDispatcher, biometricService,
- LOG_NUM_RECENT_OPERATIONS);
-
- mCurrentUserRetriever = currentUserRetriever;
- mUserSwitchCallback = userSwitchCallback;
- }
-
- public UserAwareBiometricScheduler(@NonNull String tag,
- @SensorType int sensorType,
- @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull CurrentUserRetriever currentUserRetriever,
- @NonNull UserSwitchCallback userSwitchCallback) {
- this(tag, new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher,
- IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
- currentUserRetriever, userSwitchCallback);
- }
-
- @Override
- protected void startNextOperationIfIdle() {
- if (mCurrentOperation != null) {
- Slog.v(TAG, "Not idle, current operation: " + mCurrentOperation);
- return;
- }
- if (mPendingOperations.isEmpty()) {
- Slog.d(TAG, "No operations, returning to idle");
- return;
- }
-
- final int currentUserId = mCurrentUserRetriever.getCurrentUserId();
- final int nextUserId = mPendingOperations.getFirst().getTargetUserId();
-
- if (nextUserId == currentUserId || mPendingOperations.getFirst().isStartUserOperation()) {
- super.startNextOperationIfIdle();
- } else if (currentUserId == UserHandle.USER_NULL) {
- final BaseClientMonitor startClient =
- mUserSwitchCallback.getStartUserClient(nextUserId);
- final ClientFinishedCallback finishedCallback =
- new ClientFinishedCallback(startClient);
-
- Slog.d(TAG, "[Starting User] " + startClient);
- mCurrentOperation = new BiometricSchedulerOperation(
- startClient, finishedCallback, STATE_STARTED);
- startClient.start(finishedCallback);
- } else {
- if (mStopUserClient != null) {
- Slog.d(TAG, "[Waiting for StopUser] " + mStopUserClient);
- } else {
- mStopUserClient = mUserSwitchCallback
- .getStopUserClient(currentUserId);
- final ClientFinishedCallback finishedCallback =
- new ClientFinishedCallback(mStopUserClient);
-
- Slog.d(TAG, "[Stopping User] current: " + currentUserId
- + ", next: " + nextUserId + ". " + mStopUserClient);
- mCurrentOperation = new BiometricSchedulerOperation(
- mStopUserClient, finishedCallback, STATE_STARTED);
- mStopUserClient.start(finishedCallback);
- }
- }
- }
-
- @Override
- public void onUserStopped() {
- if (mStopUserClient == null) {
- Slog.e(TAG, "Unexpected onUserStopped");
- return;
- }
-
- Slog.d(TAG, "[OnUserStopped]: " + mStopUserClient);
- mStopUserClient.onUserStopped();
- mStopUserClient = null;
- }
-
- @VisibleForTesting
- @Nullable public StopUserClient<?> getStopUserClient() {
- return mStopUserClient;
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index a946af8..bd6d593 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -72,9 +72,6 @@
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.face.aidl.FaceProvider;
-import com.android.server.biometrics.sensors.face.hidl.Face10;
-
-import com.google.android.collect.Lists;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -664,60 +661,11 @@
provider.second.scheduleGetFeature(provider.first, token, userId, feature,
new ClientMonitorCallbackConverter(receiver), opPackageName);
}
- @NonNull
- private List<ServiceProvider> getHidlProviders(
- @NonNull List<FaceSensorPropertiesInternal> hidlSensors) {
- final List<ServiceProvider> providers = new ArrayList<>();
-
- for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) {
- providers.add(
- Face10.newInstance(getContext(), mBiometricStateCallback,
- mAuthenticationStateListeners, hidlSensor,
- mLockoutResetDispatcher));
- }
-
- return providers;
- }
-
- @NonNull
- private List<ServiceProvider> getAidlProviders(@NonNull List<String> instances) {
- final List<ServiceProvider> providers = new ArrayList<>();
-
- for (String instance : instances) {
- final FaceProvider provider = mFaceProvider.apply(instance);
- Slog.i(TAG, "Adding AIDL provider: " + instance);
- providers.add(provider);
- }
-
- return providers;
- }
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
public void registerAuthenticators(
- @NonNull List<FaceSensorPropertiesInternal> hidlSensors) {
- super.registerAuthenticators_enforcePermission();
-
- mRegistry.registerAll(() -> {
- List<String> aidlSensors = new ArrayList<>();
- final String[] instances = mAidlInstanceNameSupplier.get();
- if (instances != null) {
- aidlSensors.addAll(Lists.newArrayList(instances));
- }
-
- final Pair<List<FaceSensorPropertiesInternal>, List<String>>
- filteredInstances = filterAvailableHalInstances(hidlSensors, aidlSensors);
-
- final List<ServiceProvider> providers = new ArrayList<>();
- providers.addAll(getHidlProviders(filteredInstances.first));
- providers.addAll(getAidlProviders(filteredInstances.second));
- return providers;
- });
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
- public void registerAuthenticatorsLegacy(
FaceSensorConfigurations faceSensorConfigurations) {
- super.registerAuthenticatorsLegacy_enforcePermission();
+ super.registerAuthenticators_enforcePermission();
if (!faceSensorConfigurations.hasSensorConfigurations()) {
Slog.d(TAG, "No face sensors to register.");
@@ -771,40 +719,6 @@
.getSensorPropForInstance(finalSensorInstance));
}
- private Pair<List<FaceSensorPropertiesInternal>, List<String>>
- filterAvailableHalInstances(
- @NonNull List<FaceSensorPropertiesInternal> hidlInstances,
- @NonNull List<String> aidlInstances) {
- if ((hidlInstances.size() + aidlInstances.size()) <= 1) {
- return new Pair(hidlInstances, aidlInstances);
- }
-
- if (Flags.faceVhalFeature()) {
- Slog.i(TAG, "Face VHAL feature is on");
- } else {
- Slog.i(TAG, "Face VHAL feature is off");
- }
-
- final int virtualAt = aidlInstances.indexOf("virtual");
- if (Flags.faceVhalFeature() && Utils.isFaceVirtualEnabled(getContext())) {
- if (virtualAt != -1) {
- //only virtual instance should be returned
- Slog.i(TAG, "virtual hal is used");
- return new Pair(new ArrayList<>(), List.of(aidlInstances.get(virtualAt)));
- } else {
- Slog.e(TAG, "Could not find virtual interface while it is enabled");
- return new Pair(hidlInstances, aidlInstances);
- }
- } else {
- //remove virtual instance
- aidlInstances = new ArrayList<>(aidlInstances);
- if (virtualAt != -1) {
- aidlInstances.remove(virtualAt);
- }
- return new Pair(hidlInstances, aidlInstances);
- }
- }
-
@Override
public void addAuthenticatorsRegisteredCallback(
IFaceAuthenticatorsRegisteredCallback callback) {
@@ -883,17 +797,13 @@
return null;
};
- if (Flags.deHidl()) {
- mFaceProviderFunction = faceProviderFunction != null ? faceProviderFunction :
- ((filteredSensorProps, resetLockoutRequiresChallenge) -> new FaceProvider(
- getContext(), mBiometricStateCallback, mAuthenticationStateListeners,
- filteredSensorProps.second,
- filteredSensorProps.first, mLockoutResetDispatcher,
- BiometricContext.getInstance(getContext()),
- resetLockoutRequiresChallenge));
- } else {
- mFaceProviderFunction = ((filteredSensorProps, resetLockoutRequiresChallenge) -> null);
- }
+ mFaceProviderFunction = faceProviderFunction != null ? faceProviderFunction :
+ ((filteredSensorProps, resetLockoutRequiresChallenge) -> new FaceProvider(
+ getContext(), mBiometricStateCallback, mAuthenticationStateListeners,
+ filteredSensorProps.second,
+ filteredSensorProps.first, mLockoutResetDispatcher,
+ BiometricContext.getInstance(getContext()),
+ resetLockoutRequiresChallenge));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
index 098be21..cf677d5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
@@ -27,7 +27,6 @@
import android.hardware.keymaster.HardwareAuthToken;
import android.util.Slog;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AcquisitionClient;
@@ -53,16 +52,6 @@
/**
* Interface to send results to the AidlResponseHandler's owner.
*/
- public interface HardwareUnavailableCallback {
- /**
- * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
- */
- void onHardwareUnavailable();
- }
-
- /**
- * Interface to send results to the AidlResponseHandler's owner.
- */
public interface AidlResponseHandlerCallback {
/**
* Invoked when enrollment is successful.
@@ -90,8 +79,6 @@
@NonNull
private final AuthSessionCoordinator mAuthSessionCoordinator;
@NonNull
- private final HardwareUnavailableCallback mHardwareUnavailableCallback;
- @NonNull
private final AidlResponseHandlerCallback mAidlResponseHandlerCallback;
public AidlResponseHandler(@NonNull Context context,
@@ -99,24 +86,6 @@
@NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull AuthSessionCoordinator authSessionCoordinator,
- @NonNull HardwareUnavailableCallback hardwareUnavailableCallback) {
- this(context, scheduler, sensorId, userId, lockoutTracker, lockoutResetDispatcher,
- authSessionCoordinator, hardwareUnavailableCallback,
- new AidlResponseHandlerCallback() {
- @Override
- public void onEnrollSuccess() {}
-
- @Override
- public void onHardwareUnavailable() {}
- });
- }
-
- public AidlResponseHandler(@NonNull Context context,
- @NonNull BiometricScheduler scheduler, int sensorId, int userId,
- @NonNull LockoutTracker lockoutTracker,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull AuthSessionCoordinator authSessionCoordinator,
- @NonNull HardwareUnavailableCallback hardwareUnavailableCallback,
@NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback) {
mContext = context;
mScheduler = scheduler;
@@ -125,7 +94,6 @@
mLockoutTracker = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
mAuthSessionCoordinator = authSessionCoordinator;
- mHardwareUnavailableCallback = hardwareUnavailableCallback;
mAidlResponseHandlerCallback = aidlResponseHandlerCallback;
}
@@ -185,11 +153,7 @@
handleResponse(ErrorConsumer.class, (c) -> {
c.onError(error, vendorCode);
if (error == Error.HW_UNAVAILABLE) {
- if (Flags.deHidl()) {
- mAidlResponseHandlerCallback.onHardwareUnavailable();
- } else {
- mHardwareUnavailableCallback.onHardwareUnavailable();
- }
+ mAidlResponseHandlerCallback.onHardwareUnavailable();
}
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 415d294..c43c7d9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.NotificationManager;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.SensorPrivacyManager;
@@ -39,7 +38,6 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -70,8 +68,6 @@
private final UsageStats mUsageStats;
@NonNull
private final AuthSessionCoordinator mAuthSessionCoordinator;
- @Nullable
- private final NotificationManager mNotificationManager;
private final int[] mBiometricPromptIgnoreList;
private final int[] mBiometricPromptIgnoreListVendor;
private final int[] mKeyguardIgnoreList;
@@ -123,7 +119,6 @@
biometricStrength);
setRequestId(requestId);
mUsageStats = usageStats;
- mNotificationManager = context.getSystemService(NotificationManager.class);
mSensorPrivacyManager = sensorPrivacyManager;
mAuthSessionCoordinator = biometricContext.getAuthSessionCoordinator();
mAuthenticationStateListeners = authenticationStateListeners;
@@ -163,11 +158,7 @@
0 /* vendorCode */);
mCallback.onClientFinished(this, false /* success */);
} else {
- if (Flags.deHidl()) {
- startAuthenticate();
- } else {
- mCancellationSignal = doAuthenticate();
- }
+ doAuthenticate();
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
@@ -176,27 +167,7 @@
}
}
- private ICancellationSignal doAuthenticate() throws RemoteException {
- final AidlSession session = getFreshDaemon();
-
- if (session.hasContextMethods()) {
- final OperationContextExt opContext = getOperationContext();
- final ICancellationSignal cancel = session.getSession().authenticateWithContext(
- mOperationId, opContext.toAidlContext(getOptions()));
- getBiometricContext().subscribe(opContext, ctx -> {
- try {
- session.getSession().onContextChanged(ctx);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify context changed", e);
- }
- });
- return cancel;
- } else {
- return session.getSession().authenticate(mOperationId);
- }
- }
-
- private void startAuthenticate() throws RemoteException {
+ private void doAuthenticate() throws RemoteException {
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 5ddddda..dcd94896 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -29,7 +29,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.BiometricsProto;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.OperationContextExt;
@@ -112,38 +111,14 @@
}
try {
- if (Flags.deHidl()) {
- startDetect();
- } else {
- mCancellationSignal = doDetectInteraction();
- }
+ doDetectInteraction();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting face detect", e);
mCallback.onClientFinished(this, false /* success */);
}
}
- private ICancellationSignal doDetectInteraction() throws RemoteException {
- final AidlSession session = getFreshDaemon();
-
- if (session.hasContextMethods()) {
- final OperationContextExt opContext = getOperationContext();
- final ICancellationSignal cancel = session.getSession().detectInteractionWithContext(
- opContext.toAidlContext(mOptions));
- getBiometricContext().subscribe(opContext, ctx -> {
- try {
- session.getSession().onContextChanged(ctx);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify context changed", e);
- }
- });
- return cancel;
- } else {
- return session.getSession().detectInteraction();
- }
- }
-
- private void startDetect() throws RemoteException {
+ private void doDetectInteraction() throws RemoteException {
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 781e3f4..73e8ece 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -36,7 +36,6 @@
import android.view.Surface;
import com.android.internal.R;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
@@ -193,11 +192,7 @@
features[i] = featureList.get(i);
}
- if (Flags.deHidl()) {
- startEnroll(features);
- } else {
- mCancellationSignal = doEnroll(features);
- }
+ doEnroll(features);
} catch (RemoteException | IllegalArgumentException e) {
Slog.e(TAG, "Exception when requesting enroll", e);
onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
@@ -205,43 +200,7 @@
}
}
- private ICancellationSignal doEnroll(byte[] features) throws RemoteException {
- final AidlSession session = getFreshDaemon();
- final HardwareAuthToken hat =
- HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken);
-
- if (session.hasContextMethods()) {
- final OperationContextExt opContext = getOperationContext();
- ICancellationSignal cancel;
- if (session.supportsFaceEnrollOptions()) {
- FaceEnrollOptions options = new FaceEnrollOptions();
- options.hardwareAuthToken = hat;
- options.enrollmentType = EnrollmentType.DEFAULT;
- options.features = features;
- options.nativeHandlePreview = null;
- options.context = opContext.toAidlContext();
- options.surfacePreview = mPreviewSurface;
- cancel = session.getSession().enrollWithOptions(options);
- } else {
- cancel = session.getSession().enrollWithContext(
- hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle,
- opContext.toAidlContext());
- }
- getBiometricContext().subscribe(opContext, ctx -> {
- try {
- session.getSession().onContextChanged(ctx);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify context changed", e);
- }
- });
- return cancel;
- } else {
- return session.getSession().enroll(hat, EnrollmentType.DEFAULT, features,
- mHwPreviewHandle);
- }
- }
-
- private void startEnroll(byte[] features) throws RemoteException {
+ private void doEnroll(byte[] features) throws RemoteException {
final AidlSession session = getFreshDaemon();
final HardwareAuthToken hat =
HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 11db183..75b4fd3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -27,11 +27,9 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
-import android.hardware.biometrics.common.ComponentInfo;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
@@ -43,7 +41,6 @@
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -56,7 +53,6 @@
import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.BiometricHandlerProvider;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -193,11 +189,7 @@
mAuthenticationStateListeners = authenticationStateListeners;
mHalInstanceName = halInstanceName;
mFaceSensors = new SensorList<>(ActivityManager.getService());
- if (Flags.deHidl()) {
- mHandler = biometricHandlerProvider.getFaceHandler();
- } else {
- mHandler = new Handler(Looper.getMainLooper());
- }
+ mHandler = biometricHandlerProvider.getFaceHandler();
mUsageStats = new UsageStats(context);
mLockoutResetDispatcher = lockoutResetDispatcher;
mActivityTaskManager = ActivityTaskManager.getInstance();
@@ -223,50 +215,15 @@
}
private void initSensors(boolean resetLockoutRequiresChallenge, SensorProps[] props) {
- if (Flags.deHidl()) {
- if (resetLockoutRequiresChallenge) {
- Slog.d(getTag(), "Adding HIDL configs");
- for (SensorProps prop : props) {
- addHidlSensors(prop, resetLockoutRequiresChallenge);
- }
- } else {
- Slog.d(getTag(), "Adding AIDL configs");
- for (SensorProps prop : props) {
- addAidlSensors(prop, resetLockoutRequiresChallenge);
- }
+ if (resetLockoutRequiresChallenge) {
+ Slog.d(getTag(), "Adding HIDL configs");
+ for (SensorProps prop : props) {
+ addHidlSensors(prop, resetLockoutRequiresChallenge);
}
} else {
+ Slog.d(getTag(), "Adding AIDL configs");
for (SensorProps prop : props) {
- final int sensorId = prop.commonProps.sensorId;
-
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- if (prop.commonProps.componentInfo != null) {
- for (ComponentInfo info : prop.commonProps.componentInfo) {
- componentInfo.add(new ComponentInfoInternal(info.componentId,
- info.hardwareVersion, info.firmwareVersion, info.serialNumber,
- info.softwareVersion));
- }
- }
-
- final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
- prop.commonProps.sensorId, prop.commonProps.sensorStrength,
- prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType,
- prop.supportsDetectInteraction, prop.halControlsPreview,
- false /* resetLockoutRequiresChallenge */);
- final Sensor sensor = new Sensor(this,
- mContext, mHandler, internalProp,
- mBiometricContext);
- sensor.init(mLockoutResetDispatcher, this);
- final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
- sensor.getLazySession().get().getUserId();
- mFaceSensors.addSensor(sensorId, sensor, userId,
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) {
- scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
- }
- });
- Slog.d(getTag(), "Added: " + internalProp);
+ addAidlSensors(prop, resetLockoutRequiresChallenge);
}
}
}
@@ -477,12 +434,7 @@
@Override
public int getLockoutModeForUser(int sensorId, int userId) {
- if (Flags.deHidl()) {
- return mFaceSensors.get(sensorId).getLockoutModeForUser(userId);
- } else {
- return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
- Utils.getCurrentStrength(sensorId));
- }
+ return mFaceSensors.get(sensorId).getLockoutModeForUser(userId);
}
@Override
@@ -492,11 +444,7 @@
@Override
public boolean isHardwareDetected(int sensorId) {
- if (Flags.deHidl()) {
- return mFaceSensors.get(sensorId).isHardwareDetected(mHalInstanceName);
- } else {
- return hasHalInstance();
- }
+ return mFaceSensors.get(sensorId).isHardwareDetected(mHalInstanceName);
}
@Override
@@ -549,23 +497,7 @@
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, maxTemplatesPerUser, debugConsent, options);
- if (Flags.deHidl()) {
- scheduleForSensor(sensorId, client, mBiometricStateCallback);
- } else {
- scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
- mBiometricStateCallback, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- ClientMonitorCallback.super.onClientFinished(clientMonitor,
- success);
- if (success) {
- scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
- scheduleInvalidationRequest(sensorId, userId);
- }
- }
- }));
- }
+ scheduleForSensor(sensorId, client, mBiometricStateCallback);
});
return id;
}
@@ -614,12 +546,8 @@
final int sensorId = options.getSensorId();
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
- final LockoutTracker lockoutTracker;
- if (Flags.deHidl()) {
- lockoutTracker = mFaceSensors.get(sensorId).getLockoutTracker(true /* forAuth */);
- } else {
- lockoutTracker = null;
- }
+ final LockoutTracker lockoutTracker = mFaceSensors.get(sensorId).getLockoutTracker(
+ true /* forAuth */);
final FaceAuthenticationClient client = new FaceAuthenticationClient(
mContext, mFaceSensors.get(sensorId).getLazySession(), token, requestId,
callback, operationId, restricted, options, cookie,
@@ -634,29 +562,18 @@
@Override
public void onClientStarted(
BaseClientMonitor clientMonitor) {
- if (Flags.deHidl()) {
- mBiometricHandlerProvider.getBiometricCallbackHandler().post(() ->
- mAuthSessionCoordinator.authStartedFor(userId, sensorId,
- requestId));
- } else {
- mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId);
- }
+ mBiometricHandlerProvider.getBiometricCallbackHandler().post(() ->
+ mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId));
}
@Override
public void onClientFinished(
BaseClientMonitor clientMonitor,
boolean success) {
- if (Flags.deHidl()) {
- mBiometricHandlerProvider.getBiometricCallbackHandler().post(() ->
- mAuthSessionCoordinator.authEndedFor(userId,
- Utils.getCurrentStrength(sensorId), sensorId, requestId,
- client.wasAuthSuccessful()));
- } else {
- mAuthSessionCoordinator.authEndedFor(userId,
- Utils.getCurrentStrength(sensorId),
- sensorId, requestId, client.wasAuthSuccessful());
- }
+ mBiometricHandlerProvider.getBiometricCallbackHandler().post(() ->
+ mAuthSessionCoordinator.authEndedFor(userId,
+ Utils.getCurrentStrength(sensorId), sensorId, requestId,
+ client.wasAuthSuccessful()));
}
});
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 635e79a..6b99493 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -42,7 +42,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -57,7 +56,6 @@
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
-import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
import com.android.server.biometrics.sensors.UserSwitchProvider;
import com.android.server.biometrics.sensors.face.FaceUtils;
@@ -89,7 +87,6 @@
@Nullable AidlSession mCurrentSession;
@NonNull BiometricContext mBiometricContext;
-
Sensor(@NonNull FaceProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
@NonNull BiometricContext biometricContext) {
@@ -116,11 +113,7 @@
*/
public void init(@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull FaceProvider provider) {
- if (Flags.deHidl()) {
- setScheduler(getBiometricSchedulerForInit(lockoutResetDispatcher, provider));
- } else {
- setScheduler(getUserAwareBiometricSchedulerForInit(lockoutResetDispatcher, provider));
- }
+ setScheduler(getBiometricSchedulerForInit(lockoutResetDispatcher, provider));
mLazySession = () -> mCurrentSession != null ? mCurrentSession : null;
mLockoutTracker = new LockoutCache();
}
@@ -149,8 +142,7 @@
final AidlResponseHandler resultController = new AidlResponseHandler(
mContext, mScheduler, sensorId, newUserId,
mLockoutTracker, lockoutResetDispatcher,
- mBiometricContext.getAuthSessionCoordinator(), () -> {
- },
+ mBiometricContext.getAuthSessionCoordinator(),
new AidlResponseHandler.AidlResponseHandlerCallback() {
@Override
public void onEnrollSuccess() {
@@ -173,40 +165,6 @@
});
}
- private UserAwareBiometricScheduler<IFace, ISession> getUserAwareBiometricSchedulerForInit(
- LockoutResetDispatcher lockoutResetDispatcher,
- FaceProvider provider) {
- return new UserAwareBiometricScheduler<>(TAG,
- BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityDispatcher */,
- () -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL,
- new UserAwareBiometricScheduler.UserSwitchCallback() {
- @NonNull
- @Override
- public StopUserClient<ISession> getStopUserClient(int userId) {
- return new FaceStopUserClient(mContext,
- () -> mLazySession.get().getSession(), mToken, userId,
- mSensorProperties.sensorId, BiometricLogger.ofUnknown(mContext),
- mBiometricContext, () -> mCurrentSession = null);
- }
-
- @NonNull
- @Override
- public StartUserClient<IFace, ISession> getStartUserClient(int newUserId) {
- final int sensorId = mSensorProperties.sensorId;
- final AidlResponseHandler resultController = new AidlResponseHandler(
- mContext, mScheduler, sensorId, newUserId,
- mLockoutTracker, lockoutResetDispatcher,
- mBiometricContext.getAuthSessionCoordinator(), () -> {
- Slog.e(TAG, "Face sensor hardware unavailable.");
- mCurrentSession = null;
- });
-
- return Sensor.this.getStartUserClient(resultController, sensorId,
- newUserId, provider);
- }
- });
- }
-
private FaceStartUserClient getStartUserClient(@NonNull AidlResponseHandler resultController,
int sensorId, int newUserId, @NonNull FaceProvider provider) {
final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
deleted file mode 100644
index 0e2367a..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ /dev/null
@@ -1,239 +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.biometrics.sensors.face.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.ITestSession;
-import android.hardware.biometrics.ITestSessionCallback;
-import android.hardware.face.Face;
-import android.hardware.face.FaceAuthenticationFrame;
-import android.hardware.face.FaceEnrollFrame;
-import android.hardware.face.FaceEnrollOptions;
-import android.hardware.face.IFaceServiceReceiver;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.sensors.BaseClientMonitor;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.face.FaceUtils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-
-public class BiometricTestSessionImpl extends ITestSession.Stub {
- private static final String TAG = "BiometricTestSessionImpl";
-
- @NonNull private final Context mContext;
- private final int mSensorId;
- @NonNull private final ITestSessionCallback mCallback;
- @NonNull private final Face10 mFace10;
- @NonNull private final Face10.HalResultController mHalResultController;
- @NonNull private final Set<Integer> mEnrollmentIds;
- @NonNull private final Random mRandom;
-
-
- private final IFaceServiceReceiver mReceiver = new IFaceServiceReceiver.Stub() {
- @Override
- public void onEnrollResult(Face face, int remaining) {
-
- }
-
- @Override
- public void onAcquired(int acquiredInfo, int vendorCode) {
-
- }
-
- @Override
- public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) {
-
- }
-
- @Override
- public void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
-
- }
-
- @Override
- public void onAuthenticationFailed() {
-
- }
-
- @Override
- public void onError(int error, int vendorCode) {
-
- }
-
- @Override
- public void onRemoved(Face face, int remaining) {
-
- }
-
- @Override
- public void onFeatureSet(boolean success, int feature) {
-
- }
-
- @Override
- public void onFeatureGet(boolean success, int[] features, boolean[] featureState) {
-
- }
-
- @Override
- public void onChallengeGenerated(int sensorId, int userId, long challenge) {
-
- }
-
- @Override
- public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
-
- }
-
- @Override
- public void onEnrollmentFrame(FaceEnrollFrame frame) {
-
- }
- };
-
- BiometricTestSessionImpl(@NonNull Context context, int sensorId,
- @NonNull ITestSessionCallback callback,
- @NonNull Face10 face10,
- @NonNull Face10.HalResultController halResultController) {
- mContext = context;
- mSensorId = sensorId;
- mCallback = callback;
- mFace10 = face10;
- mHalResultController = halResultController;
- mEnrollmentIds = new HashSet<>();
- mRandom = new Random();
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void setTestHalEnabled(boolean enabled) {
-
- super.setTestHalEnabled_enforcePermission();
-
- mFace10.setTestHalEnabled(enabled);
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void startEnroll(int userId) {
-
- super.startEnroll_enforcePermission();
-
- mFace10.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), new int[0] /* disabledFeatures */,
- null /* previewSurface */, false /* debugConsent */,
- (new FaceEnrollOptions.Builder()).build());
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void finishEnroll(int userId) {
-
- super.finishEnroll_enforcePermission();
-
- int nextRandomId = mRandom.nextInt();
- while (mEnrollmentIds.contains(nextRandomId)) {
- nextRandomId = mRandom.nextInt();
- }
-
- mEnrollmentIds.add(nextRandomId);
- mHalResultController.onEnrollResult(0 /* deviceId */,
- nextRandomId /* faceId */, userId, 0);
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void acceptAuthentication(int userId) {
-
- // Fake authentication with any of the existing fingers
- super.acceptAuthentication_enforcePermission();
-
- List<Face> faces = FaceUtils.getLegacyInstance(mSensorId)
- .getBiometricsForUser(mContext, userId);
- if (faces.isEmpty()) {
- Slog.w(TAG, "No faces, returning");
- return;
- }
- final int fid = faces.get(0).getBiometricId();
- final ArrayList<Byte> hat = new ArrayList<>(Collections.nCopies(69, (byte) 0));
- mHalResultController.onAuthenticated(0 /* deviceId */, fid, userId, hat);
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void rejectAuthentication(int userId) {
-
- super.rejectAuthentication_enforcePermission();
-
- mHalResultController.onAuthenticated(0 /* deviceId */, 0 /* faceId */, userId, null);
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void notifyAcquired(int userId, int acquireInfo) {
-
- super.notifyAcquired_enforcePermission();
-
- mHalResultController.onAcquired(0 /* deviceId */, userId, acquireInfo, 0 /* vendorCode */);
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void notifyError(int userId, int errorCode) {
-
- super.notifyError_enforcePermission();
-
- mHalResultController.onError(0 /* deviceId */, userId, errorCode, 0 /* vendorCode */);
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void cleanupInternalState(int userId) {
-
- super.cleanupInternalState_enforcePermission();
-
- mFace10.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- try {
- mCallback.onCleanupStarted(clientMonitor.getTargetUserId());
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
-
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- try {
- mCallback.onCleanupFinished(clientMonitor.getTargetUserId());
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
- });
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
deleted file mode 100644
index 306ddfa..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ /dev/null
@@ -1,1342 +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.biometrics.sensors.face.hidl;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.SynchronousUserSwitchObserver;
-import android.app.UserSwitchObserver;
-import android.content.Context;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.biometrics.ITestSession;
-import android.hardware.biometrics.ITestSessionCallback;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
-import android.hardware.face.Face;
-import android.hardware.face.FaceAuthenticateOptions;
-import android.hardware.face.FaceEnrollOptions;
-import android.hardware.face.FaceSensorPropertiesInternal;
-import android.hardware.face.IFaceServiceReceiver;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IHwBinder;
-import android.os.Looper;
-import android.os.NativeHandle;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-import android.view.Surface;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
-import com.android.server.biometrics.AuthenticationStatsCollector;
-import com.android.server.biometrics.Flags;
-import com.android.server.biometrics.SensorServiceStateProto;
-import com.android.server.biometrics.SensorStateProto;
-import com.android.server.biometrics.UserStateProto;
-import com.android.server.biometrics.Utils;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.AcquisitionClient;
-import com.android.server.biometrics.sensors.AuthSessionCoordinator;
-import com.android.server.biometrics.sensors.AuthenticationConsumer;
-import com.android.server.biometrics.sensors.AuthenticationStateListeners;
-import com.android.server.biometrics.sensors.BaseClientMonitor;
-import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.BiometricStateCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
-import com.android.server.biometrics.sensors.EnumerateConsumer;
-import com.android.server.biometrics.sensors.ErrorConsumer;
-import com.android.server.biometrics.sensors.LockoutResetDispatcher;
-import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.PerformanceTracker;
-import com.android.server.biometrics.sensors.RemovalConsumer;
-import com.android.server.biometrics.sensors.face.FaceUtils;
-import com.android.server.biometrics.sensors.face.LockoutHalImpl;
-import com.android.server.biometrics.sensors.face.ServiceProvider;
-import com.android.server.biometrics.sensors.face.UsageStats;
-import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
-import com.android.server.biometrics.sensors.face.aidl.AidlSession;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.time.Clock;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.function.Supplier;
-
-/**
- * Supports a single instance of the {@link android.hardware.biometrics.face.V1_0} or its extended
- * minor versions.
- */
-public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
-
- private static final String TAG = "Face10";
-
- private static final int ENROLL_TIMEOUT_SEC = 75;
- private static final int GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS = 60 * 1000;
- private static final int GENERATE_CHALLENGE_COUNTER_TTL_MILLIS =
- FaceGenerateChallengeClient.CHALLENGE_TIMEOUT_SEC * 1000;
- @VisibleForTesting
- public static Clock sSystemClock = Clock.systemUTC();
-
- private boolean mTestHalEnabled;
-
- @NonNull private final FaceSensorPropertiesInternal mSensorProperties;
- @NonNull private final BiometricStateCallback mBiometricStateCallback;
- @NonNull
- private final AuthenticationStateListeners mAuthenticationStateListeners;
- @NonNull private final Context mContext;
- @NonNull private final BiometricScheduler<IBiometricsFace, AidlSession> mScheduler;
- @NonNull private final Handler mHandler;
- @NonNull private final Supplier<IBiometricsFace> mLazyDaemon;
- @NonNull private final LockoutHalImpl mLockoutTracker;
- @NonNull private final UsageStats mUsageStats;
- @NonNull private final Map<Integer, Long> mAuthenticatorIds;
- @Nullable private IBiometricsFace mDaemon;
- @NonNull private final HalResultController mHalResultController;
- @NonNull private final BiometricContext mBiometricContext;
- @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector;
- // for requests that do not use biometric prompt
- @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
- private int mCurrentUserId = UserHandle.USER_NULL;
- private final int mSensorId;
- private final List<Long> mGeneratedChallengeCount = new ArrayList<>();
- private final LockoutResetDispatcher mLockoutResetDispatcher;
- private FaceGenerateChallengeClient mGeneratedChallengeCache = null;
- private AidlSession mSession;
-
- private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) {
- scheduleInternalCleanup(newUserId, null /* callback */);
- scheduleGetFeature(mSensorId, new Binder(), newUserId,
- BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION,
- null, mContext.getOpPackageName());
- }
- };
-
- public static class HalResultController extends IBiometricsFaceClientCallback.Stub {
- /**
- * Interface to sends results to the HalResultController's owner.
- */
- public interface Callback {
- /**
- * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
- */
- void onHardwareUnavailable();
- }
-
- private final int mSensorId;
- @NonNull private final Context mContext;
- @NonNull private final Handler mHandler;
- @NonNull private final BiometricScheduler<IBiometricsFace, AidlSession> mScheduler;
- @Nullable private Callback mCallback;
- @NonNull private final LockoutHalImpl mLockoutTracker;
- @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
-
-
- HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
- @NonNull BiometricScheduler<IBiometricsFace, AidlSession> scheduler,
- @NonNull LockoutHalImpl lockoutTracker,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
- mSensorId = sensorId;
- mContext = context;
- mHandler = handler;
- mScheduler = scheduler;
- mLockoutTracker = lockoutTracker;
- mLockoutResetDispatcher = lockoutResetDispatcher;
- }
-
- public void setCallback(@Nullable Callback callback) {
- mCallback = callback;
- }
-
- @Override
- public void onEnrollResult(long deviceId, int faceId, int userId, int remaining) {
- mHandler.post(() -> {
- final CharSequence name = FaceUtils.getLegacyInstance(mSensorId)
- .getUniqueName(mContext, userId);
- final Face face = new Face(name, faceId, deviceId);
-
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceEnrollClient)) {
- Slog.e(TAG, "onEnrollResult for non-enroll client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceEnrollClient enrollClient = (FaceEnrollClient) client;
- enrollClient.onEnrollResult(face, remaining);
- });
- }
-
- @Override
- public void onAuthenticated(long deviceId, int faceId, int userId,
- ArrayList<Byte> token) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(TAG, "onAuthenticated for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final boolean authenticated = faceId != 0;
- final Face face = new Face("", faceId, deviceId);
- authenticationConsumer.onAuthenticated(face, authenticated, token);
- });
- }
-
- @Override
- public void onAcquired(long deviceId, int userId, int acquiredInfo,
- int vendorCode) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AcquisitionClient)) {
- Slog.e(TAG, "onAcquired for non-acquire client: "
- + Utils.getClientName(client));
- return;
- }
-
- final AcquisitionClient<?> acquisitionClient =
- (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(acquiredInfo, vendorCode);
- });
- }
-
- @Override
- public void onError(long deviceId, int userId, int error, int vendorCode) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- Slog.d(TAG, "handleError"
- + ", client: " + (client != null ? client.getOwnerString() : null)
- + ", error: " + error
- + ", vendorCode: " + vendorCode);
- if (!(client instanceof ErrorConsumer)) {
- Slog.e(TAG, "onError for non-error consumer: " + Utils.getClientName(
- client));
- return;
- }
-
- final ErrorConsumer errorConsumer = (ErrorConsumer) client;
- errorConsumer.onError(error, vendorCode);
-
- if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
- Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE");
- if (mCallback != null) {
- mCallback.onHardwareUnavailable();
- }
- }
- });
- }
-
- @Override
- public void onRemoved(long deviceId, ArrayList<Integer> removed, int userId) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof RemovalConsumer)) {
- Slog.e(TAG, "onRemoved for non-removal consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final RemovalConsumer removalConsumer = (RemovalConsumer) client;
-
- if (!removed.isEmpty()) {
- // Convert to old fingerprint-like behavior, where remove() receives
- // one removal at a time. This way, remove can share some more common code.
- for (int i = 0; i < removed.size(); i++) {
- final int id = removed.get(i);
- final Face face = new Face("", id, deviceId);
- final int remaining = removed.size() - i - 1;
- Slog.d(TAG, "Removed, faceId: " + id + ", remaining: " + remaining);
- removalConsumer.onRemoved(face, remaining);
- }
- } else {
- removalConsumer.onRemoved(null, 0 /* remaining */);
- }
-
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0, UserHandle.USER_CURRENT);
- });
- }
-
- @Override
- public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof EnumerateConsumer)) {
- Slog.e(TAG, "onEnumerate for non-enumerate consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
-
- if (!faceIds.isEmpty()) {
- // Convert to old fingerprint-like behavior, where enumerate() receives one
- // template at a time. This way, enumerate can share some more common code.
- for (int i = 0; i < faceIds.size(); i++) {
- final Face face = new Face("", faceIds.get(i), deviceId);
- enumerateConsumer.onEnumerationResult(face, faceIds.size() - i - 1);
- }
- } else {
- // For face, the HIDL contract is to receive an empty list when there are no
- // templates enrolled. Send a null identifier since we don't consume them
- // anywhere, and send remaining == 0 so this code can be shared with Face@1.1
- enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
- }
- });
- }
-
- @Override
- public void onLockoutChanged(long duration) {
- mHandler.post(() -> {
- Slog.d(TAG, "onLockoutChanged: " + duration);
- final @LockoutTracker.LockoutMode int lockoutMode;
- if (duration == 0) {
- lockoutMode = LockoutTracker.LOCKOUT_NONE;
- } else if (duration == -1 || duration == Long.MAX_VALUE) {
- lockoutMode = LockoutTracker.LOCKOUT_PERMANENT;
- } else {
- lockoutMode = LockoutTracker.LOCKOUT_TIMED;
- }
-
- mLockoutTracker.setCurrentUserLockoutMode(lockoutMode);
-
- if (duration == 0) {
- mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorId);
- }
- });
- }
- }
-
- @VisibleForTesting
- Face10(@NonNull Context context,
- @NonNull BiometricStateCallback biometricStateCallback,
- @NonNull AuthenticationStateListeners authenticationStateListeners,
- @NonNull FaceSensorPropertiesInternal sensorProps,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull Handler handler,
- @NonNull BiometricScheduler<IBiometricsFace, AidlSession> scheduler,
- @NonNull BiometricContext biometricContext) {
- mSensorProperties = sensorProps;
- mContext = context;
- mBiometricStateCallback = biometricStateCallback;
- mAuthenticationStateListeners = authenticationStateListeners;
- mSensorId = sensorProps.sensorId;
- mScheduler = scheduler;
- mHandler = handler;
- mBiometricContext = biometricContext;
- mUsageStats = new UsageStats(context);
- mAuthenticatorIds = new HashMap<>();
- mLazyDaemon = Face10.this::getDaemon;
- mLockoutTracker = new LockoutHalImpl();
- mHalResultController = new HalResultController(sensorProps.sensorId, context, mHandler,
- mScheduler, mLockoutTracker, lockoutResetDispatcher);
- mLockoutResetDispatcher = lockoutResetDispatcher;
- mHalResultController.setCallback(() -> {
- mDaemon = null;
- mCurrentUserId = UserHandle.USER_NULL;
- });
-
- AuthenticationStatsBroadcastReceiver mBroadcastReceiver =
- new AuthenticationStatsBroadcastReceiver(
- mContext,
- BiometricsProtoEnums.MODALITY_FACE,
- (AuthenticationStatsCollector collector) -> {
- Slog.d(TAG, "Initializing AuthenticationStatsCollector");
- mAuthenticationStatsCollector = collector;
- });
-
- try {
- ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to register user switch observer");
- }
- }
-
- public static Face10 newInstance(@NonNull Context context,
- @NonNull BiometricStateCallback biometricStateCallback,
- @NonNull AuthenticationStateListeners authenticationStateListeners,
- @NonNull FaceSensorPropertiesInternal sensorProps,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
- final Handler handler = new Handler(Looper.getMainLooper());
- return new Face10(context, biometricStateCallback, authenticationStateListeners,
- sensorProps, lockoutResetDispatcher, handler, new BiometricScheduler<>(
- BiometricScheduler.SENSOR_TYPE_FACE,
- null /* gestureAvailabilityTracker */),
- BiometricContext.getInstance(context));
- }
-
- @Override
- public void serviceDied(long cookie) {
- Slog.e(TAG, "HAL died");
- mHandler.post(() -> {
- PerformanceTracker.getInstanceForSensorId(mSensorId)
- .incrementHALDeathCount();
- mDaemon = null;
- mCurrentUserId = UserHandle.USER_NULL;
-
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (client instanceof ErrorConsumer) {
- Slog.e(TAG, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
- final ErrorConsumer errorConsumer = (ErrorConsumer) client;
- errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
-
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- BiometricsProtoEnums.MODALITY_FACE,
- BiometricsProtoEnums.ISSUE_HAL_DEATH,
- -1 /* sensorId */);
- }
-
- mScheduler.recordCrashState();
- mScheduler.reset();
- });
- }
-
- public int getCurrentUserId() {
- return mCurrentUserId;
- }
-
- synchronized AidlSession getSession() {
- if (mDaemon != null && mSession != null) {
- return mSession;
- } else {
- return mSession = new AidlSession(mContext, this::getDaemon, mCurrentUserId,
- new AidlResponseHandler(mContext, mScheduler, mSensorId,
- mCurrentUserId, mLockoutTracker, mLockoutResetDispatcher,
- new AuthSessionCoordinator(), () -> {
- mDaemon = null;
- mCurrentUserId = UserHandle.USER_NULL;
- }));
- }
- }
-
- private synchronized IBiometricsFace getDaemon() {
- if (mTestHalEnabled) {
- final TestHal testHal = new TestHal(mContext, mSensorId);
- testHal.setCallback(mHalResultController);
- return testHal;
- }
-
- if (mDaemon != null) {
- return mDaemon;
- }
-
- Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
- + mScheduler.getCurrentClient());
-
- try {
- mDaemon = IBiometricsFace.getService();
- } catch (java.util.NoSuchElementException e) {
- // Service doesn't exist or cannot be opened.
- Slog.w(TAG, "NoSuchElementException", e);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get face HAL", e);
- }
-
- if (mDaemon == null) {
- Slog.w(TAG, "Face HAL not available");
- return null;
- }
-
- mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
-
- // HAL ID for these HIDL versions are only used to determine if callbacks have been
- // successfully set.
- long halId = 0;
- try {
- halId = mDaemon.setCallback(mHalResultController).value;
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to set callback for face HAL", e);
- mDaemon = null;
- }
-
- Slog.d(TAG, "Face HAL ready, HAL ID: " + halId);
- if (halId != 0) {
- scheduleLoadAuthenticatorIds();
- scheduleInternalCleanup(ActivityManager.getCurrentUser(), null /* callback */);
- scheduleGetFeature(mSensorId, new Binder(),
- ActivityManager.getCurrentUser(),
- BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION, null,
- mContext.getOpPackageName());
- } else {
- Slog.e(TAG, "Unable to set callback");
- mDaemon = null;
- }
-
- return mDaemon;
- }
-
- @Override
- public boolean containsSensor(int sensorId) {
- return mSensorId == sensorId;
- }
-
- @Override
- @NonNull
- public List<FaceSensorPropertiesInternal> getSensorProperties() {
- final List<FaceSensorPropertiesInternal> properties = new ArrayList<>();
- properties.add(mSensorProperties);
- return properties;
- }
-
- @NonNull
- @Override
- public FaceSensorPropertiesInternal getSensorProperties(int sensorId) {
- return mSensorProperties;
- }
-
- @Override
- @NonNull
- public List<Face> getEnrolledFaces(int sensorId, int userId) {
- return FaceUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId);
- }
-
- @Override
- public boolean hasEnrollments(int sensorId, int userId) {
- return !getEnrolledFaces(sensorId, userId).isEmpty();
- }
-
- @Override
- @LockoutTracker.LockoutMode
- public int getLockoutModeForUser(int sensorId, int userId) {
- return mLockoutTracker.getLockoutModeForUser(userId);
- }
-
- @Override
- public long getAuthenticatorId(int sensorId, int userId) {
- return mAuthenticatorIds.getOrDefault(userId, 0L);
- }
-
- @Override
- public boolean isHardwareDetected(int sensorId) {
- return getDaemon() != null;
- }
-
- private boolean isGeneratedChallengeCacheValid() {
- return mGeneratedChallengeCache != null
- && sSystemClock.millis() - mGeneratedChallengeCache.getCreatedAt()
- < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS;
- }
-
- private void incrementChallengeCount() {
- mGeneratedChallengeCount.add(0, sSystemClock.millis());
- }
-
- private int decrementChallengeCount() {
- final long now = sSystemClock.millis();
- // ignore values that are old in case generate/revoke calls are not matched
- // this doesn't ensure revoke if calls are mismatched but it keeps the list from growing
- mGeneratedChallengeCount.removeIf(x -> now - x > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS);
- if (!mGeneratedChallengeCount.isEmpty()) {
- mGeneratedChallengeCount.remove(0);
- }
- return mGeneratedChallengeCount.size();
- }
-
- /**
- * {@link IBiometricsFace} only supports a single in-flight challenge but there are cases where
- * two callers both need challenges (e.g. resetLockout right before enrollment).
- */
- @Override
- public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
- @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
-
- if (Flags.deHidl()) {
- scheduleGenerateChallengeAidl(userId, token, receiver, opPackageName);
- } else {
- scheduleGenerateChallengeHidl(userId, token, receiver, opPackageName);
- }
- });
- }
-
- private void scheduleGenerateChallengeAidl(int userId, @NonNull IBinder token,
- @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
- final com.android.server.biometrics.sensors.face.aidl.FaceGenerateChallengeClient client =
- new com.android.server.biometrics.sensors.face.aidl.FaceGenerateChallengeClient(
- mContext, this::getSession, token,
- new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- if (client != clientMonitor) {
- Slog.e(TAG,
- "scheduleGenerateChallenge onClientStarted, mismatched client."
- + " Expecting: " + client + ", received: "
- + clientMonitor);
- }
- }
- });
- }
-
- private void scheduleGenerateChallengeHidl(int userId, @NonNull IBinder token,
- @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
- incrementChallengeCount();
- if (isGeneratedChallengeCacheValid()) {
- Slog.d(TAG, "Current challenge is cached and will be reused");
- mGeneratedChallengeCache.reuseResult(receiver);
- return;
- }
-
- final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, sSystemClock.millis());
- mGeneratedChallengeCache = client;
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- if (client != clientMonitor) {
- Slog.e(TAG,
- "scheduleGenerateChallenge onClientStarted, mismatched client."
- + " Expecting: " + client + ", received: "
- + clientMonitor);
- }
- }
- });
- }
-
- @Override
- public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
- @NonNull String opPackageName, long challenge) {
- mHandler.post(() -> {
- if (Flags.deHidl()) {
- scheduleRevokeChallengeAidl(userId, token, opPackageName);
- } else {
- scheduleRevokeChallengeHidl(userId, token, opPackageName);
- }
- });
- }
-
- private void scheduleRevokeChallengeAidl(int userId, @NonNull IBinder token,
- @NonNull String opPackageName) {
- final com.android.server.biometrics.sensors.face.aidl.FaceRevokeChallengeClient
- client =
- new com.android.server.biometrics.sensors.face.aidl.FaceRevokeChallengeClient(
- mContext, this::getSession, token, userId, opPackageName, mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector), mBiometricContext, 0L);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- if (client != clientMonitor) {
- Slog.e(TAG,
- "scheduleRevokeChallenge, mismatched client." + "Expecting: "
- + client + ", received: " + clientMonitor);
- }
- }
- });
- }
-
- private void scheduleRevokeChallengeHidl(int userId, @NonNull IBinder token,
- @NonNull String opPackageName) {
- final boolean shouldRevoke = decrementChallengeCount() == 0;
- if (!shouldRevoke) {
- Slog.w(TAG, "scheduleRevokeChallenge skipped - challenge still in use: "
- + mGeneratedChallengeCount);
- return;
- }
-
- Slog.d(TAG, "scheduleRevokeChallenge executing - no active clients");
- mGeneratedChallengeCache = null;
- final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
- mLazyDaemon, token, userId, opPackageName, mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- if (client != clientMonitor) {
- Slog.e(TAG,
- "scheduleRevokeChallenge, mismatched client." + "Expecting: "
- + client + ", received: " + clientMonitor);
- }
- }
- });
- }
-
- @Override
- public long scheduleEnroll(int sensorId, @NonNull IBinder token,
- @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
- @NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable Surface previewSurface, boolean debugConsent,
- @NonNull FaceEnrollOptions options) {
- final long id = mRequestCounter.incrementAndGet();
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
- if (Flags.deHidl()) {
- scheduleEnrollAidl(token, hardwareAuthToken, userId, receiver,
- opPackageName, disabledFeatures, previewSurface, id, options);
- } else {
- scheduleEnrollHidl(token, hardwareAuthToken, userId, receiver,
- opPackageName, disabledFeatures, previewSurface, id, options);
- }
- });
- return id;
- }
-
- private void scheduleEnrollAidl(@NonNull IBinder token,
- @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
- @NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable Surface previewSurface, long id,
- @NonNull FaceEnrollOptions options) {
- final com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient client =
- new com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient(
- mContext, this::getSession, token,
- new ClientMonitorCallbackConverter(receiver), userId,
- hardwareAuthToken, opPackageName, id,
- FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
- ENROLL_TIMEOUT_SEC, previewSurface, mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector), mBiometricContext,
- mContext.getResources().getInteger(
- com.android.internal.R.integer.config_faceMaxTemplatesPerUser),
- false, options);
-
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- mBiometricStateCallback.onClientStarted(clientMonitor);
- }
-
- @Override
- public void onBiometricAction(int action) {
- mBiometricStateCallback.onBiometricAction(action);
- }
-
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- mBiometricStateCallback.onClientFinished(clientMonitor, success);
- if (success) {
- // Update authenticatorIds
- scheduleUpdateActiveUserWithoutHandler(client.getTargetUserId());
- }
- }
- });
- }
-
- private void scheduleEnrollHidl(@NonNull IBinder token,
- @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
- @NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable Surface previewSurface, long id, FaceEnrollOptions options) {
- final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
- ENROLL_TIMEOUT_SEC, previewSurface, mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, options);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- mBiometricStateCallback.onClientStarted(clientMonitor);
- }
-
- @Override
- public void onBiometricAction(int action) {
- mBiometricStateCallback.onBiometricAction(action);
- }
-
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- mBiometricStateCallback.onClientFinished(clientMonitor, success);
- if (success) {
- // Update authenticatorIds
- scheduleUpdateActiveUserWithoutHandler(client.getTargetUserId());
- }
- }
- });
- }
-
- @Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
- mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
- }
-
- @Override
- public long scheduleFaceDetect(@NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter callback,
- @NonNull FaceAuthenticateOptions options, int statsClient) {
- throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did you"
- + "forget to check the supportsFaceDetection flag?");
- }
-
- @Override
- public void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId) {
- throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did you"
- + "forget to check the supportsFaceDetection flag?");
- }
-
- @Override
- public void scheduleAuthenticate(@NonNull IBinder token, long operationId,
- int cookie, @NonNull ClientMonitorCallbackConverter receiver,
- @NonNull FaceAuthenticateOptions options, long requestId, boolean restricted,
- int statsClient, boolean allowBackgroundAuthentication) {
- mHandler.post(() -> {
- final int userId = options.getUserId();
- scheduleUpdateActiveUserWithoutHandler(userId);
-
- final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorId);
- if (Flags.deHidl()) {
- scheduleAuthenticateAidl(token, operationId, cookie, receiver, options, requestId,
- restricted, statsClient, allowBackgroundAuthentication, isStrongBiometric);
- } else {
- scheduleAuthenticateHidl(token, operationId, cookie, receiver, options, requestId,
- restricted, statsClient, allowBackgroundAuthentication, isStrongBiometric);
- }
- });
- }
-
- private void scheduleAuthenticateAidl(@NonNull IBinder token, long operationId,
- int cookie, @NonNull ClientMonitorCallbackConverter receiver,
- @NonNull FaceAuthenticateOptions options, long requestId, boolean restricted,
- int statsClient, boolean allowBackgroundAuthentication, boolean isStrongBiometric) {
- final com.android.server.biometrics.sensors.face.aidl.FaceAuthenticationClient
- client =
- new com.android.server.biometrics.sensors.face.aidl.FaceAuthenticationClient(
- mContext, this::getSession, token, requestId, receiver, operationId,
- restricted, options, cookie, false /* requireConfirmation */,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- mAuthenticationStatsCollector), mBiometricContext,
- isStrongBiometric, mUsageStats, mLockoutTracker,
- allowBackgroundAuthentication, Utils.getCurrentStrength(mSensorId),
- mAuthenticationStateListeners);
- mScheduler.scheduleClientMonitor(client);
- }
-
- private void scheduleAuthenticateHidl(@NonNull IBinder token, long operationId,
- int cookie, @NonNull ClientMonitorCallbackConverter receiver,
- @NonNull FaceAuthenticateOptions options, long requestId, boolean restricted,
- int statsClient, boolean allowBackgroundAuthentication, boolean isStrongBiometric) {
- final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
- mLazyDaemon, token, requestId, receiver, operationId, restricted, options,
- cookie, false /* requireConfirmation */,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- mAuthenticationStatsCollector), mBiometricContext,
- isStrongBiometric, mLockoutTracker, mUsageStats,
- allowBackgroundAuthentication, Utils.getCurrentStrength(mSensorId),
- mAuthenticationStateListeners);
- mScheduler.scheduleClientMonitor(client);
- }
-
- @Override
- public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
- int cookie, @NonNull ClientMonitorCallbackConverter receiver,
- @NonNull FaceAuthenticateOptions options, boolean restricted, int statsClient,
- boolean allowBackgroundAuthentication) {
- final long id = mRequestCounter.incrementAndGet();
-
- scheduleAuthenticate(token, operationId, cookie, receiver,
- options, id, restricted, statsClient, allowBackgroundAuthentication);
-
- return id;
- }
-
- @Override
- public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) {
- mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token, requestId));
- }
-
- @Override
- public void scheduleRemove(int sensorId, @NonNull IBinder token, int faceId, int userId,
- @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
-
- if (Flags.deHidl()) {
- scheduleRemoveAidl(token, userId, receiver, opPackageName, faceId);
- } else {
- scheduleRemoveHidl(token, userId, receiver, opPackageName, faceId);
- }
- });
- }
-
- @Override
- public void scheduleRemoveAll(int sensorId, @NonNull IBinder token, int userId,
- @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
-
- // For IBiometricsFace@1.0, remove(0) means remove all enrollments
- if (Flags.deHidl()) {
- scheduleRemoveAidl(token, userId, receiver, opPackageName, 0);
- } else {
- scheduleRemoveHidl(token, userId, receiver, opPackageName, 0);
- }
- });
- }
-
- private void scheduleRemoveAidl(@NonNull IBinder token, int userId,
- @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, int faceId) {
- final com.android.server.biometrics.sensors.face.aidl.FaceRemovalClient client =
- new com.android.server.biometrics.sensors.face.aidl.FaceRemovalClient(
- mContext, this::getSession, token,
- new ClientMonitorCallbackConverter(receiver), new int[]{faceId}, userId,
- opPackageName, FaceUtils.getLegacyInstance(mSensorId), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector), mBiometricContext,
- mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
- }
-
- private void scheduleRemoveHidl(@NonNull IBinder token, int userId,
- @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, int faceId) {
- final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), faceId, userId,
- opPackageName, FaceUtils.getLegacyInstance(mSensorId), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
- }
-
- @Override
- public void scheduleResetLockout(int sensorId, int userId, @NonNull byte[] hardwareAuthToken) {
- mHandler.post(() -> {
- if (getEnrolledFaces(sensorId, userId).isEmpty()) {
- Slog.w(TAG, "Ignoring lockout reset, no templates enrolled for user: " + userId);
- return;
- }
-
- scheduleUpdateActiveUserWithoutHandler(userId);
- if (Flags.deHidl()) {
- scheduleResetLockoutAidl(userId, hardwareAuthToken);
- } else {
- scheduleResetLockoutHidl(userId, hardwareAuthToken);
- }
- });
- }
-
- private void scheduleResetLockoutAidl(int userId,
- @NonNull byte[] hardwareAuthToken) {
- final com.android.server.biometrics.sensors.face.aidl.FaceResetLockoutClient client =
- new com.android.server.biometrics.sensors.face.aidl.FaceResetLockoutClient(
- mContext, this::getSession, userId, mContext.getOpPackageName(),
- mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext, hardwareAuthToken, mLockoutTracker,
- mLockoutResetDispatcher, mSensorProperties.sensorStrength);
- mScheduler.scheduleClientMonitor(client);
- }
-
- private void scheduleResetLockoutHidl(int userId,
- @NonNull byte[] hardwareAuthToken) {
- final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext,
- mLazyDaemon,
- userId, mContext.getOpPackageName(), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, hardwareAuthToken);
- mScheduler.scheduleClientMonitor(client);
- }
-
- @Override
- public void scheduleSetFeature(int sensorId, @NonNull IBinder token, int userId, int feature,
- boolean enabled, @NonNull byte[] hardwareAuthToken,
- @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
- if (Flags.deHidl()) {
- scheduleSetFeatureAidl(sensorId, token, userId, feature, enabled, hardwareAuthToken,
- receiver, opPackageName);
- } else {
- scheduleSetFeatureHidl(sensorId, token, userId, feature, enabled, hardwareAuthToken,
- receiver, opPackageName);
- }
- });
- }
-
- private void scheduleSetFeatureHidl(int sensorId, @NonNull IBinder token, int userId,
- int feature, boolean enabled, @NonNull byte[] hardwareAuthToken,
- @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
- final List<Face> faces = getEnrolledFaces(sensorId, userId);
- if (faces.isEmpty()) {
- Slog.w(TAG, "Ignoring setFeature, no templates enrolled for user: " + userId);
- return;
- }
- final int faceId = faces.get(0).getBiometricId();
- final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext, mLazyDaemon,
- token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- mSensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext, feature,
- enabled, hardwareAuthToken, faceId);
- mScheduler.scheduleClientMonitor(client);
- }
-
- private void scheduleSetFeatureAidl(int sensorId, @NonNull IBinder token, int userId,
- int feature, boolean enabled, @NonNull byte[] hardwareAuthToken,
- @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
- final com.android.server.biometrics.sensors.face.aidl.FaceSetFeatureClient client =
- new com.android.server.biometrics.sensors.face.aidl.FaceSetFeatureClient(
- mContext, this::getSession, token,
- new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- mSensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext,
- feature, enabled, hardwareAuthToken);
- mScheduler.scheduleClientMonitor(client);
- }
-
-
- @Override
- public void scheduleGetFeature(int sensorId, @NonNull IBinder token, int userId, int feature,
- @Nullable ClientMonitorCallbackConverter listener, @NonNull String opPackageName) {
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
-
- if (Flags.deHidl()) {
- scheduleGetFeatureAidl(token, userId, feature, listener,
- opPackageName);
- } else {
- scheduleGetFeatureHidl(sensorId, token, userId, feature, listener,
- opPackageName);
- }
- });
- }
-
- private void scheduleGetFeatureHidl(int sensorId, @NonNull IBinder token, int userId,
- int feature, @Nullable ClientMonitorCallbackConverter listener,
- @NonNull String opPackageName) {
- final List<Face> faces = getEnrolledFaces(sensorId, userId);
- if (faces.isEmpty()) {
- Slog.w(TAG, "Ignoring getFeature, no templates enrolled for user: " + userId);
- return;
- }
-
- final int faceId = faces.get(0).getBiometricId();
- final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
- token, listener, userId, opPackageName, mSensorId,
- BiometricLogger.ofUnknown(mContext), mBiometricContext, feature, faceId);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- if (success
- && feature == BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION) {
- final int settingsValue = client.getValue() ? 1 : 0;
- Slog.d(TAG,
- "Updating attention value for user: " + userId + " to value: "
- + settingsValue);
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, settingsValue,
- userId);
- }
- }
- });
- }
-
- private void scheduleGetFeatureAidl(@NonNull IBinder token, int userId,
- int feature, @Nullable ClientMonitorCallbackConverter listener,
- @NonNull String opPackageName) {
- final com.android.server.biometrics.sensors.face.aidl.FaceGetFeatureClient client =
- new com.android.server.biometrics.sensors.face.aidl.FaceGetFeatureClient(
- mContext, this::getSession, token, listener, userId, opPackageName,
- mSensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext,
- feature);
- mScheduler.scheduleClientMonitor(client);
- }
-
- private void scheduleInternalCleanup(int userId,
- @Nullable ClientMonitorCallback callback) {
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
- if (Flags.deHidl()) {
- scheduleInternalCleanupAidl(userId, callback);
- } else {
- scheduleInternalCleanupHidl(userId, callback);
- }
- });
- }
-
- private void scheduleInternalCleanupHidl(int userId,
- @Nullable ClientMonitorCallback callback) {
- final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
- mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, FaceUtils.getLegacyInstance(mSensorId),
- mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client,
- new ClientMonitorCompositeCallback(callback, mBiometricStateCallback));
- }
-
- private void scheduleInternalCleanupAidl(int userId,
- @Nullable ClientMonitorCallback callback) {
- final com.android.server.biometrics.sensors.face.aidl.FaceInternalCleanupClient
- client =
- new com.android.server.biometrics.sensors.face.aidl.FaceInternalCleanupClient(
- mContext, this::getSession, userId, mContext.getOpPackageName(),
- mSensorId, createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, FaceUtils.getLegacyInstance(mSensorId),
- mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client,
- new ClientMonitorCompositeCallback(callback, mBiometricStateCallback));
- }
-
- @Override
- public void scheduleInternalCleanup(int sensorId, int userId,
- @Nullable ClientMonitorCallback callback) {
- scheduleInternalCleanup(userId, mBiometricStateCallback);
- }
-
- @Override
- public void scheduleInternalCleanup(int sensorId, int userId,
- @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments) {
- scheduleInternalCleanup(userId, callback);
- }
-
- @Override
- public void startPreparedClient(int sensorId, int cookie) {
- mHandler.post(() -> {
- mScheduler.startPreparedClient(cookie);
- });
- }
-
- @Override
- public void dumpProtoState(int sensorId, ProtoOutputStream proto,
- boolean clearSchedulerBuffer) {
- final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
-
- proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
- proto.write(SensorStateProto.MODALITY, SensorStateProto.FACE);
- proto.write(SensorStateProto.CURRENT_STRENGTH,
- Utils.getCurrentStrength(mSensorProperties.sensorId));
- proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
-
- for (UserInfo user : UserManager.get(mContext).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
-
- final long userToken = proto.start(SensorStateProto.USER_STATES);
- proto.write(UserStateProto.USER_ID, userId);
- proto.write(UserStateProto.NUM_ENROLLED, FaceUtils.getLegacyInstance(mSensorId)
- .getBiometricsForUser(mContext, userId).size());
- proto.end(userToken);
- }
-
- proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN,
- mSensorProperties.resetLockoutRequiresHardwareAuthToken);
- proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE,
- mSensorProperties.resetLockoutRequiresChallenge);
-
- proto.end(sensorToken);
- }
-
- @Override
- public void dumpProtoMetrics(int sensorId, FileDescriptor fd) {
- }
-
- @Override
- public void dumpInternal(int sensorId, PrintWriter pw) {
- PerformanceTracker performanceTracker =
- PerformanceTracker.getInstanceForSensorId(mSensorId);
-
- JSONObject dump = new JSONObject();
- try {
- dump.put("service", TAG);
-
- JSONArray sets = new JSONArray();
- for (UserInfo user : UserManager.get(mContext).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
- final int c = FaceUtils.getLegacyInstance(mSensorId)
- .getBiometricsForUser(mContext, userId).size();
- JSONObject set = new JSONObject();
- set.put("id", userId);
- set.put("count", c);
- set.put("accept", performanceTracker.getAcceptForUser(userId));
- set.put("reject", performanceTracker.getRejectForUser(userId));
- set.put("acquire", performanceTracker.getAcquireForUser(userId));
- set.put("lockout", performanceTracker.getTimedLockoutForUser(userId));
- set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId));
- // cryptoStats measures statistics about secure face transactions
- // (e.g. to unlock password storage, make secure purchases, etc.)
- set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId));
- set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId));
- set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId));
- sets.put(set);
- }
-
- dump.put("prints", sets);
- } catch (JSONException e) {
- Slog.e(TAG, "dump formatting failure", e);
- }
- pw.println(dump);
- pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
-
- mScheduler.dump(pw);
- mUsageStats.print(pw);
- }
-
- private void scheduleLoadAuthenticatorIds() {
- // Note that this can be performed on the scheduler (as opposed to being done immediately
- // when the HAL is (re)loaded, since
- // 1) If this is truly the first time it's being performed (e.g. system has just started),
- // this will be run very early and way before any applications need to generate keys.
- // 2) If this is being performed to refresh the authenticatorIds (e.g. HAL crashed and has
- // just been reloaded), the framework already has a cache of the authenticatorIds. This
- // is safe because authenticatorIds only change when A) new template has been enrolled,
- // or B) all templates are removed.
- mHandler.post(() -> {
- for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
- final int targetUserId = user.id;
- if (!mAuthenticatorIds.containsKey(targetUserId)) {
- scheduleUpdateActiveUserWithoutHandler(targetUserId);
- }
- }
- });
- }
-
- /**
- * Schedules the {@link FaceUpdateActiveUserClient} without posting the work onto the handler.
- * Many/most APIs are user-specific. However, the HAL requires explicit "setActiveUser"
- * invocation prior to authenticate/enroll/etc. Thus, internally we usually want to schedule
- * this operation on the same lambda/runnable as those operations so that the ordering is
- * correct.
- */
- private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
- final boolean hasEnrolled = !getEnrolledFaces(mSensorId, targetUserId).isEmpty();
- final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
- mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, hasEnrolled, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- if (success) {
- if (mCurrentUserId != targetUserId) {
- // Create new session with updated user ID
- mSession = null;
- }
- mCurrentUserId = targetUserId;
- } else {
- Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
- }
- }
- });
- }
-
- private BiometricLogger createLogger(int statsAction, int statsClient,
- AuthenticationStatsCollector authenticationStatsCollector) {
- return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE,
- statsAction, statsClient, authenticationStatsCollector);
- }
-
- /**
- * Sends a debug message to the HAL with the provided FileDescriptor and arguments.
- */
- public void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args) {
- // WARNING: CDD restricts image data from leaving TEE unencrypted on
- // production devices:
- // [C-1-10] MUST not allow unencrypted access to identifiable biometric
- // data or any data derived from it (such as embeddings) to the
- // Application Processor outside the context of the TEE.
- // As such, this API should only be enabled for testing purposes on
- // engineering and userdebug builds. All modules in the software stack
- // MUST enforce final build products do NOT have this functionality.
- // Additionally, the following check MUST NOT be removed.
- if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
- return;
- }
-
- // Additionally, this flag allows turning off face for a device
- // (either permanently through the build or on an individual device).
- if (SystemProperties.getBoolean("ro.face.disable_debug_data", false)
- || SystemProperties.getBoolean("persist.face.disable_debug_data", false)) {
- return;
- }
-
- // The debug method takes two file descriptors. The first is for text
- // output, which we will drop. The second is for binary data, which
- // will be the protobuf data.
- final IBiometricsFace daemon = getDaemon();
- if (daemon != null) {
- FileOutputStream devnull = null;
- try {
- devnull = new FileOutputStream("/dev/null");
- final NativeHandle handle = new NativeHandle(
- new FileDescriptor[]{devnull.getFD(), fd},
- new int[0], false);
- daemon.debug(handle, new ArrayList<String>(Arrays.asList(args)));
- } catch (IOException | RemoteException ex) {
- Slog.d(TAG, "error while reading face debugging data", ex);
- } finally {
- if (devnull != null) {
- try {
- devnull.close();
- } catch (IOException ex) {
- }
- }
- }
- }
- }
-
- void setTestHalEnabled(boolean enabled) {
- mTestHalEnabled = enabled;
- }
-
- @NonNull
- @Override
- public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
- @NonNull String opPackageName) {
- return new BiometricTestSessionImpl(mContext, mSensorId, callback,
- this, mHalResultController);
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
deleted file mode 100644
index e44b263..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ /dev/null
@@ -1,254 +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.biometrics.sensors.face.hidl;
-
-import static android.adaptiveauth.Flags.reportBiometricAuthAttempts;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.content.res.Resources;
-import android.hardware.SensorPrivacyManager;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricManager.Authenticators;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.hardware.face.FaceAuthenticateOptions;
-import android.hardware.face.FaceManager;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.internal.R;
-import com.android.server.biometrics.Utils;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.AuthenticationClient;
-import com.android.server.biometrics.sensors.AuthenticationStateListeners;
-import com.android.server.biometrics.sensors.BiometricNotificationUtils;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
-import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.PerformanceTracker;
-import com.android.server.biometrics.sensors.face.UsageStats;
-
-import java.util.ArrayList;
-import java.util.function.Supplier;
-
-/**
- * Face-specific authentication client supporting the {@link android.hardware.biometrics.face.V1_0}
- * HIDL interface.
- */
-class FaceAuthenticationClient
- extends AuthenticationClient<IBiometricsFace, FaceAuthenticateOptions> {
-
- private static final String TAG = "FaceAuthenticationClient";
-
- private final UsageStats mUsageStats;
-
- private final int[] mBiometricPromptIgnoreList;
- private final int[] mBiometricPromptIgnoreListVendor;
- private final int[] mKeyguardIgnoreList;
- private final int[] mKeyguardIgnoreListVendor;
-
- private int mLastAcquire;
- private SensorPrivacyManager mSensorPrivacyManager;
- @NonNull
- private final AuthenticationStateListeners mAuthenticationStateListeners;
-
- FaceAuthenticationClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFace> lazyDaemon,
- @NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener, long operationId,
- boolean restricted, @NonNull FaceAuthenticateOptions options, int cookie,
- boolean requireConfirmation,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- boolean isStrongBiometric, @NonNull LockoutTracker lockoutTracker,
- @NonNull UsageStats usageStats, boolean allowBackgroundAuthentication,
- @Authenticators.Types int sensorStrength,
- @NonNull AuthenticationStateListeners authenticationStateListeners) {
- super(context, lazyDaemon, token, listener, operationId, restricted,
- options, cookie, requireConfirmation, logger, biometricContext,
- isStrongBiometric, null /* taskStackListener */,
- lockoutTracker, allowBackgroundAuthentication, false /* shouldVibrate */,
- sensorStrength);
- setRequestId(requestId);
- mUsageStats = usageStats;
- mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
- mAuthenticationStateListeners = authenticationStateListeners;
-
- final Resources resources = getContext().getResources();
- mBiometricPromptIgnoreList = resources.getIntArray(
- R.array.config_face_acquire_biometricprompt_ignorelist);
- mBiometricPromptIgnoreListVendor = resources.getIntArray(
- R.array.config_face_acquire_vendor_biometricprompt_ignorelist);
- mKeyguardIgnoreList = resources.getIntArray(
- R.array.config_face_acquire_keyguard_ignorelist);
- mKeyguardIgnoreListVendor = resources.getIntArray(
- R.array.config_face_acquire_vendor_keyguard_ignorelist);
- }
-
- @Override
- public void start(@NonNull ClientMonitorCallback callback) {
- super.start(callback);
- mState = STATE_STARTED;
- }
-
- @NonNull
- @Override
- protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
- return new ClientMonitorCompositeCallback(
- getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
- }
-
- @Override
- protected void startHalOperation() {
-
- if (mSensorPrivacyManager != null
- && mSensorPrivacyManager
- .isSensorPrivacyEnabled(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
- SensorPrivacyManager.Sensors.CAMERA)) {
- onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mCallback.onClientFinished(this, false /* success */);
- return;
- }
-
- try {
- getFreshDaemon().authenticate(mOperationId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting auth", e);
- onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- @Override
- protected void stopHalOperation() {
- try {
- getFreshDaemon().cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- @Override
- public boolean wasUserDetected() {
- // Do not provide haptic feedback if the user was not detected, and an error (usually
- // ERROR_TIMEOUT) is received.
- return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED
- && mLastAcquire != FaceManager.FACE_ACQUIRED_SENSOR_DIRTY;
- }
-
- @Override
- protected void handleLifecycleAfterAuth(boolean authenticated) {
- // For face, the authentication lifecycle ends either when
- // 1) Authenticated == true
- // 2) Error occurred
- // 3) Authenticated == false
- mCallback.onClientFinished(this, true /* success */);
- }
-
- @Override
- public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
- @LockoutTracker.LockoutMode final int lockoutMode =
- getLockoutTracker().getLockoutModeForUser(userId);
- final PerformanceTracker performanceTracker =
- PerformanceTracker.getInstanceForSensorId(getSensorId());
- if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
- performanceTracker.incrementPermanentLockoutForUser(userId);
- } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
- performanceTracker.incrementTimedLockoutForUser(userId);
- }
-
- return lockoutMode;
- }
-
- @Override
- public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated, ArrayList<Byte> token) {
- super.onAuthenticated(identifier, authenticated, token);
-
- mState = STATE_STOPPED;
- mUsageStats.addEvent(new UsageStats.AuthenticationEvent(
- getStartTimeMs(),
- System.currentTimeMillis() - getStartTimeMs() /* latency */,
- authenticated,
- 0 /* error */,
- 0 /* vendorError */,
- getTargetUserId()));
-
- if (reportBiometricAuthAttempts()) {
- if (authenticated) {
- mAuthenticationStateListeners.onAuthenticationSucceeded(getRequestReason(),
- getTargetUserId());
- } else {
- mAuthenticationStateListeners.onAuthenticationFailed(getRequestReason(),
- getTargetUserId());
- }
- }
- }
-
- @Override
- public void onError(@BiometricConstants.Errors int error, int vendorCode) {
- mUsageStats.addEvent(new UsageStats.AuthenticationEvent(
- getStartTimeMs(),
- System.currentTimeMillis() - getStartTimeMs() /* latency */,
- false /* authenticated */,
- error,
- vendorCode,
- getTargetUserId()));
-
- super.onError(error, vendorCode);
- }
-
- private int[] getAcquireIgnorelist() {
- return isBiometricPrompt() ? mBiometricPromptIgnoreList : mKeyguardIgnoreList;
- }
-
- private int[] getAcquireVendorIgnorelist() {
- return isBiometricPrompt() ? mBiometricPromptIgnoreListVendor : mKeyguardIgnoreListVendor;
- }
-
- private boolean shouldSend(int acquireInfo, int vendorCode) {
- if (acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR) {
- return !Utils.listContains(getAcquireVendorIgnorelist(), vendorCode);
- } else {
- return !Utils.listContains(getAcquireIgnorelist(), acquireInfo);
- }
- }
-
- @Override
- public void onAcquired(int acquireInfo, int vendorCode) {
- mLastAcquire = acquireInfo;
-
- if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) {
- BiometricNotificationUtils.showReEnrollmentNotification(getContext());
- }
- @LockoutTracker.LockoutMode final int lockoutMode =
- getLockoutTracker().getLockoutModeForUser(getTargetUserId());
- if (lockoutMode == LockoutTracker.LOCKOUT_NONE) {
- PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
- pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
- }
-
- final boolean shouldSend = shouldSend(acquireInfo, vendorCode);
- onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
deleted file mode 100644
index 815cf91..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ /dev/null
@@ -1,155 +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.biometrics.sensors.face.hidl;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.hardware.biometrics.face.V1_0.Status;
-import android.hardware.face.Face;
-import android.hardware.face.FaceEnrollOptions;
-import android.hardware.face.FaceManager;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-import android.view.Surface;
-
-import com.android.internal.R;
-import com.android.server.biometrics.Utils;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.BiometricNotificationUtils;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
-import com.android.server.biometrics.sensors.EnrollClient;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.function.Supplier;
-
-/**
- * Face-specific enroll client supporting the {@link android.hardware.biometrics.face.V1_0} HIDL
- * interface.
- */
-public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
-
- private static final String TAG = "FaceEnrollClient";
-
- @NonNull private final int[] mDisabledFeatures;
- @NonNull private final int[] mEnrollIgnoreList;
- @NonNull private final int[] mEnrollIgnoreListVendor;
-
- FaceEnrollClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon,
- @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull byte[] hardwareAuthToken, @NonNull String owner, long requestId,
- @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
- @Nullable Surface previewSurface, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull FaceEnrollOptions options) {
- super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext,
- BiometricFaceConstants.reasonToMetric(options.getEnrollReason()));
- setRequestId(requestId);
- mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
- mEnrollIgnoreList = getContext().getResources()
- .getIntArray(R.array.config_face_acquire_enroll_ignorelist);
- mEnrollIgnoreListVendor = getContext().getResources()
- .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist);
-
- Slog.w(TAG, "EnrollOptions "
- + FaceEnrollOptions.enrollReasonToString(options.getEnrollReason()));
- }
-
- @Override
- public void start(@NonNull ClientMonitorCallback callback) {
- super.start(callback);
-
- BiometricNotificationUtils.cancelFaceEnrollNotification(getContext());
- BiometricNotificationUtils.cancelFaceReEnrollNotification(getContext());
- }
-
- @NonNull
- @Override
- protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
- return new ClientMonitorCompositeCallback(
- getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
- }
-
- @Override
- protected boolean hasReachedEnrollmentLimit() {
- final int limit = getContext().getResources().getInteger(
- com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
- final int enrolled = mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId())
- .size();
- if (enrolled >= limit) {
- Slog.w(TAG, "Too many faces registered, user: " + getTargetUserId());
- return true;
- }
- return false;
- }
-
- @Override
- public void onAcquired(int acquireInfo, int vendorCode) {
- final boolean shouldSend;
- if (acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR) {
- shouldSend = !Utils.listContains(mEnrollIgnoreListVendor, vendorCode);
- } else {
- shouldSend = !Utils.listContains(mEnrollIgnoreList, acquireInfo);
- }
- onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
- }
-
- @Override
- protected void startHalOperation() {
- final ArrayList<Byte> token = new ArrayList<>();
- for (byte b : mHardwareAuthToken) {
- token.add(b);
- }
- final ArrayList<Integer> disabledFeatures = new ArrayList<>();
- for (int disabledFeature : mDisabledFeatures) {
- disabledFeatures.add(disabledFeature);
- }
-
- try {
- final int status = getFreshDaemon().enroll(token, mTimeoutSec, disabledFeatures);
-
- if (status != Status.OK) {
- onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
- mCallback.onClientFinished(this, false /* success */);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting enroll", e);
- onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- @Override
- protected void stopHalOperation() {
- try {
- getFreshDaemon().cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
deleted file mode 100644
index 97838a7..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
+++ /dev/null
@@ -1,113 +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.biometrics.sensors.face.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.hardware.face.IFaceServiceReceiver;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.internal.util.Preconditions;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.GenerateChallengeClient;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Supplier;
-
-/**
- * Face-specific generateChallenge client supporting the
- * {@link android.hardware.biometrics.face.V1_0} HIDL interface.
- */
-public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiometricsFace> {
-
- private static final String TAG = "FaceGenerateChallengeClient";
- static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
- private static final ClientMonitorCallback EMPTY_CALLBACK = new ClientMonitorCallback() {
- };
-
- private final long mCreatedAt;
- private List<IFaceServiceReceiver> mWaiting;
- private Long mChallengeResult;
-
- FaceGenerateChallengeClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId, @NonNull BiometricLogger logger,
- @NonNull BiometricContext biometricContext, long now) {
- super(context, lazyDaemon, token, listener, userId, owner, sensorId, logger,
- biometricContext);
- mCreatedAt = now;
- mWaiting = new ArrayList<>();
- }
-
- @Override
- protected void startHalOperation() {
- mChallengeResult = null;
- try {
- mChallengeResult = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
- // send the result to the original caller via mCallback and any waiting callers
- // that called reuseResult
- sendChallengeResult(getListener(), mCallback);
- for (IFaceServiceReceiver receiver : mWaiting) {
- sendChallengeResult(new ClientMonitorCallbackConverter(receiver), EMPTY_CALLBACK);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "generateChallenge failed", e);
- mCallback.onClientFinished(this, false /* success */);
- } finally {
- mWaiting = null;
- }
- }
-
- /** @return An arbitrary time value for caching provided to the constructor. */
- public long getCreatedAt() {
- return mCreatedAt;
- }
-
- /**
- * Reuse the result of this operation when it is available. The receiver will be notified
- * immediately if a challenge has already been generated.
- *
- * @param receiver receiver to be notified of challenge result
- */
- public void reuseResult(@NonNull IFaceServiceReceiver receiver) {
- if (mWaiting != null) {
- mWaiting.add(receiver);
- } else {
- sendChallengeResult(new ClientMonitorCallbackConverter(receiver), EMPTY_CALLBACK);
- }
- }
-
- private void sendChallengeResult(@NonNull ClientMonitorCallbackConverter receiver,
- @NonNull ClientMonitorCallback ownerCallback) {
- Preconditions.checkState(mChallengeResult != null, "result not available");
- try {
- receiver.onChallengeGenerated(getSensorId(), getTargetUserId(), mChallengeResult);
- ownerCallback.onClientFinished(this, true /* success */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- ownerCallback.onClientFinished(this, false /* success */);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
deleted file mode 100644
index 47aaeec..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
+++ /dev/null
@@ -1,102 +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.biometrics.sensors.face.hidl;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.hardware.biometrics.face.V1_0.OptionalBool;
-import android.hardware.biometrics.face.V1_0.Status;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.BiometricsProto;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.HalClientMonitor;
-
-import java.util.function.Supplier;
-
-/**
- * Face-specific getFeature client supporting the {@link android.hardware.biometrics.face.V1_0}
- * HIDL interface.
- */
-public class FaceGetFeatureClient extends HalClientMonitor<IBiometricsFace> {
-
- private static final String TAG = "FaceGetFeatureClient";
-
- private final int mFeature;
- private final int mFaceId;
- private boolean mValue;
-
- FaceGetFeatureClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon,
- @NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- int feature, int faceId) {
- super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- logger, biometricContext);
- mFeature = feature;
- mFaceId = faceId;
- }
-
- @Override
- public void unableToStart() {
- try {
- getListener().onFeatureGet(false /* success */, new int[0], new boolean[0]);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send error", e);
- }
- }
-
- @Override
- public void start(@NonNull ClientMonitorCallback callback) {
- super.start(callback);
- startHalOperation();
- }
-
- @Override
- protected void startHalOperation() {
- try {
- final OptionalBool result = getFreshDaemon().getFeature(mFeature, mFaceId);
- int[] features = new int[1];
- boolean[] featureState = new boolean[1];
- features[0] = mFeature;
- featureState[0] = result.value;
- mValue = result.value;
-
- getListener().onFeatureGet(result.status == Status.OK, features, featureState);
- mCallback.onClientFinished(this, true /* success */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to getFeature", e);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- boolean getValue() {
- return mValue;
- }
-
- @Override
- public int getProtoEnum() {
- return BiometricsProto.CM_GET_FEATURE;
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
deleted file mode 100644
index 89a17c6..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
+++ /dev/null
@@ -1,72 +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.biometrics.sensors.face.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.hardware.face.Face;
-import android.os.IBinder;
-
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.InternalCleanupClient;
-import com.android.server.biometrics.sensors.InternalEnumerateClient;
-import com.android.server.biometrics.sensors.RemovalClient;
-
-import java.util.List;
-import java.util.Map;
-import java.util.function.Supplier;
-
-/**
- * Face-specific internal cleanup client supporting the
- * {@link android.hardware.biometrics.face.V1_0} HIDL interface.
- */
-class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsFace> {
-
- FaceInternalCleanupClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, @NonNull BiometricLogger logger,
- @NonNull BiometricContext biometricContext,
- @NonNull BiometricUtils<Face> utils, @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
- utils, authenticatorIds);
- }
-
- @Override
- protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context,
- Supplier<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner,
- List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
- return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
- enrolledList, utils, sensorId, logger, biometricContext);
- }
-
- @Override
- protected RemovalClient<Face, IBiometricsFace> getRemovalClient(Context context,
- Supplier<IBiometricsFace> lazyDaemon, IBinder token,
- int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- Map<Integer, Long> authenticatorIds) {
- // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
- // is all done internally.
- return new FaceRemovalClient(context, lazyDaemon, token,
- null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
- sensorId, logger, biometricContext, authenticatorIds);
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java
deleted file mode 100644
index 250dd7e..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java
+++ /dev/null
@@ -1,60 +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.biometrics.sensors.face.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.hardware.face.Face;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.InternalEnumerateClient;
-
-import java.util.List;
-import java.util.function.Supplier;
-
-/**
- * Face-specific internal enumerate client supporting the
- * {@link android.hardware.biometrics.face.V1_0} HIDL interface.
- */
-class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFace> {
- private static final String TAG = "FaceInternalEnumerateClient";
-
- FaceInternalEnumerateClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId,
- @NonNull String owner, @NonNull List<Face> enrolledList,
- @NonNull BiometricUtils<Face> utils, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
- super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
- logger, biometricContext);
- }
-
- @Override
- protected void startHalOperation() {
- try {
- getFreshDaemon().enumerate();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting enumerate", e);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java
deleted file mode 100644
index 0ee7a35..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java
+++ /dev/null
@@ -1,65 +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.biometrics.sensors.face.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.hardware.face.Face;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.RemovalClient;
-
-import java.util.Map;
-import java.util.function.Supplier;
-
-/**
- * Face-specific removal client supporting the {@link android.hardware.biometrics.face.V1_0}
- * HIDL interface.
- */
-class FaceRemovalClient extends RemovalClient<Face, IBiometricsFace> {
- private static final String TAG = "FaceRemovalClient";
-
- private final int mBiometricId;
-
- FaceRemovalClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon,
- @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
- int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<Face> utils,
- int sensorId, @NonNull BiometricLogger logger,
- @NonNull BiometricContext biometricContext,
- @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId, logger,
- biometricContext, authenticatorIds);
- mBiometricId = biometricId;
- }
-
- @Override
- protected void startHalOperation() {
- try {
- getFreshDaemon().remove(mBiometricId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting remove", e);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
deleted file mode 100644
index f29b9e8..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
+++ /dev/null
@@ -1,87 +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.biometrics.sensors.face.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.BiometricsProto;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.HalClientMonitor;
-
-import java.util.ArrayList;
-import java.util.function.Supplier;
-
-/**
- * Face-specific resetLockout client supporting the {@link android.hardware.biometrics.face.V1_0}
- * HIDL interface.
- */
-public class FaceResetLockoutClient extends HalClientMonitor<IBiometricsFace> {
-
- private static final String TAG = "FaceResetLockoutClient";
-
- private final ArrayList<Byte> mHardwareAuthToken;
-
- FaceResetLockoutClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, String owner, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull byte[] hardwareAuthToken) {
- super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, logger, biometricContext);
-
- mHardwareAuthToken = new ArrayList<>();
- for (byte b : hardwareAuthToken) {
- mHardwareAuthToken.add(b);
- }
- }
-
- @Override
- public void unableToStart() {
- // Nothing to do here
- }
-
- @Override
- public void start(@NonNull ClientMonitorCallback callback) {
- super.start(callback);
- startHalOperation();
- }
-
- public boolean interruptsPrecedingClients() {
- return true;
- }
-
- @Override
- protected void startHalOperation() {
- try {
- getFreshDaemon().resetLockout(mHardwareAuthToken);
- mCallback.onClientFinished(this, true /* success */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to reset lockout", e);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- @Override
- public int getProtoEnum() {
- return BiometricsProto.CM_RESET_LOCKOUT;
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
deleted file mode 100644
index b7b0dc04..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
+++ /dev/null
@@ -1,57 +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.biometrics.sensors.face.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.RevokeChallengeClient;
-
-import java.util.function.Supplier;
-
-/**
- * Face-specific revokeChallenge client supporting the {@link android.hardware.biometrics.face.V1_0}
- * HIDL interface.
- */
-public class FaceRevokeChallengeClient extends RevokeChallengeClient<IBiometricsFace> {
-
- private static final String TAG = "FaceRevokeChallengeClient";
-
- FaceRevokeChallengeClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
- int userId, @NonNull String owner, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
- super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext);
- }
-
- @Override
- protected void startHalOperation() {
- try {
- getFreshDaemon().revokeChallenge();
- mCallback.onClientFinished(this, true /* success */);
- } catch (RemoteException e) {
- Slog.e(TAG, "revokeChallenge failed", e);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
deleted file mode 100644
index 3c82f9c..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
+++ /dev/null
@@ -1,100 +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.biometrics.sensors.face.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.hardware.biometrics.face.V1_0.Status;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.BiometricsProto;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.HalClientMonitor;
-
-import java.util.ArrayList;
-import java.util.function.Supplier;
-
-/**
- * Face-specific setFeature client supporting the {@link android.hardware.biometrics.face.V1_0}
- * HIDL interface.
- */
-public class FaceSetFeatureClient extends HalClientMonitor<IBiometricsFace> {
-
- private static final String TAG = "FaceSetFeatureClient";
-
- private final int mFeature;
- private final boolean mEnabled;
- private final ArrayList<Byte> mHardwareAuthToken;
- private final int mFaceId;
-
- FaceSetFeatureClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon,
- @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- int feature, boolean enabled, byte[] hardwareAuthToken, int faceId) {
- super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
- logger, biometricContext);
- mFeature = feature;
- mEnabled = enabled;
- mFaceId = faceId;
-
- mHardwareAuthToken = new ArrayList<>();
- for (byte b : hardwareAuthToken) {
- mHardwareAuthToken.add(b);
- }
- }
-
- @Override
- public void unableToStart() {
- try {
- getListener().onFeatureSet(false /* success */, mFeature);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send error", e);
- }
- }
-
- @Override
- public void start(@NonNull ClientMonitorCallback callback) {
- super.start(callback);
-
- startHalOperation();
- }
-
- @Override
- protected void startHalOperation() {
- try {
- final int result = getFreshDaemon()
- .setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId);
- getListener().onFeatureSet(result == Status.OK, mFeature);
- mCallback.onClientFinished(this, true /* success */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to set feature: " + mFeature + " to enabled: " + mEnabled, e);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- @Override
- public int getProtoEnum() {
- return BiometricsProto.CM_SET_FEATURE;
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
index a004cae4..9a4c29d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
@@ -173,9 +173,14 @@
}
private AidlResponseHandler getAidlResponseHandler() {
- return new AidlResponseHandler(getContext(), getScheduler(), getSensorProperties().sensorId,
- mCurrentUserId, mLockoutTracker, mLockoutResetDispatcher,
- mAuthSessionCoordinator, () -> {}, mAidlResponseHandlerCallback);
+ return new AidlResponseHandler(getContext(),
+ getScheduler(),
+ getSensorProperties().sensorId,
+ mCurrentUserId,
+ mLockoutTracker,
+ mLockoutResetDispatcher,
+ mAuthSessionCoordinator,
+ mAidlResponseHandlerCallback);
}
private IBiometricsFace getIBiometricsFace() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java
index fa95361..45d0cfe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java
@@ -53,7 +53,7 @@
private static final String TAG = "HidlToAidlSessionAdapter";
- private static final int CHALLENGE_TIMEOUT_SEC = 600;
+ @VisibleForTesting static final int CHALLENGE_TIMEOUT_SEC = 600;
@DurationMillisLong
private static final int GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS = 60 * 1000;
@DurationMillisLong
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index d762914..deda93c7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -72,7 +72,6 @@
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
import android.util.EventLog;
import android.util.Pair;
import android.util.Slog;
@@ -83,7 +82,6 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.AuthenticationStateListeners;
@@ -94,12 +92,8 @@
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
-import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21;
-import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21UdfpsMock;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
-import com.google.android.collect.Lists;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -886,9 +880,9 @@
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
- public void registerAuthenticatorsLegacy(
+ public void registerAuthenticators(
@NonNull FingerprintSensorConfigurations fingerprintSensorConfigurations) {
- super.registerAuthenticatorsLegacy_enforcePermission();
+ super.registerAuthenticators_enforcePermission();
if (!fingerprintSensorConfigurations.hasSensorConfigurations()) {
Slog.d(TAG, "No fingerprint sensors available.");
return;
@@ -897,30 +891,6 @@
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
- @Override // Binder call
- public void registerAuthenticators(
- @NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) {
- super.registerAuthenticators_enforcePermission();
-
- mRegistry.registerAll(() -> {
- List<String> aidlSensors = new ArrayList<>();
- final String[] instances = mAidlInstanceNameSupplier.get();
- if (instances != null) {
- aidlSensors.addAll(Lists.newArrayList(instances));
- }
-
- final Pair<List<FingerprintSensorPropertiesInternal>, List<String>>
- filteredInstances = filterAvailableHalInstances(hidlSensors, aidlSensors);
-
- final List<ServiceProvider> providers = new ArrayList<>();
- providers.addAll(getHidlProviders(filteredInstances.first));
- providers.addAll(getAidlProviders(filteredInstances.second));
-
- return providers;
- });
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void addAuthenticatorsRegisteredCallback(
IFingerprintAuthenticatorsRegisteredCallback callback) {
@@ -1086,22 +1056,15 @@
return null;
};
- if (Flags.deHidl()) {
- mFingerprintProviderFunction = fingerprintProviderFunction == null
- ? (filteredSensorProps, resetLockoutRequiresHardwareAuthToken) ->
- new FingerprintProvider(
- getContext(), mBiometricStateCallback,
- mAuthenticationStateListeners,
- filteredSensorProps.second,
- filteredSensorProps.first, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher,
- mBiometricContext,
- resetLockoutRequiresHardwareAuthToken)
- : fingerprintProviderFunction;
- } else {
- mFingerprintProviderFunction =
- (filteredSensorProps, resetLockoutRequiresHardwareAuthToken) -> null;
- }
+ mFingerprintProviderFunction = fingerprintProviderFunction != null
+ ? fingerprintProviderFunction :
+ (filteredSensorProps, resetLockoutRequiresHardwareAuthToken) ->
+ new FingerprintProvider(getContext(), mBiometricStateCallback,
+ mAuthenticationStateListeners, filteredSensorProps.second,
+ filteredSensorProps.first, mLockoutResetDispatcher,
+ mGestureAvailabilityDispatcher, mBiometricContext,
+ resetLockoutRequiresHardwareAuthToken);
+
mHandler = new Handler(Looper.getMainLooper());
mRegistry = new FingerprintServiceRegistry(mServiceWrapper, biometricServiceSupplier);
mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
@@ -1160,74 +1123,6 @@
.getSensorPropForInstance(finalSensorInstance));
}
- private Pair<List<FingerprintSensorPropertiesInternal>, List<String>>
- filterAvailableHalInstances(
- @NonNull List<FingerprintSensorPropertiesInternal> hidlInstances,
- @NonNull List<String> aidlInstances) {
- if ((hidlInstances.size() + aidlInstances.size()) <= 1) {
- return new Pair(hidlInstances, aidlInstances);
- }
-
- final int virtualAt = aidlInstances.indexOf("virtual");
- if (Utils.isFingerprintVirtualEnabled(getContext())) {
- if (virtualAt != -1) {
- //only virtual instance should be returned
- Slog.i(TAG, "virtual hal is used");
- return new Pair(new ArrayList<>(), List.of(aidlInstances.get(virtualAt)));
- } else {
- Slog.e(TAG, "Could not find virtual interface while it is enabled");
- return new Pair(hidlInstances, aidlInstances);
- }
- } else {
- //remove virtual instance
- aidlInstances = new ArrayList<>(aidlInstances);
- if (virtualAt != -1) {
- aidlInstances.remove(virtualAt);
- }
- return new Pair(hidlInstances, aidlInstances);
- }
- }
-
- @NonNull
- private List<ServiceProvider> getHidlProviders(
- @NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) {
- final List<ServiceProvider> providers = new ArrayList<>();
-
- for (FingerprintSensorPropertiesInternal hidlSensor : hidlSensors) {
- final Fingerprint21 fingerprint21;
- if ((Build.IS_USERDEBUG || Build.IS_ENG)
- && getContext().getResources().getBoolean(R.bool.allow_test_udfps)
- && Settings.Secure.getIntForUser(getContext().getContentResolver(),
- Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
- UserHandle.USER_CURRENT) != 0) {
- fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(),
- mBiometricStateCallback, mAuthenticationStateListeners,
- hidlSensor, mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
- BiometricContext.getInstance(getContext()));
- } else {
- fingerprint21 = Fingerprint21.newInstance(getContext(),
- mBiometricStateCallback, mAuthenticationStateListeners, hidlSensor,
- mHandler, mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
- }
- providers.add(fingerprint21);
- }
-
- return providers;
- }
-
- @NonNull
- private List<ServiceProvider> getAidlProviders(@NonNull List<String> instances) {
- final List<ServiceProvider> providers = new ArrayList<>();
-
- for (String instance : instances) {
- final FingerprintProvider provider = mFingerprintProvider.apply(instance);
- Slog.i(TAG, "Adding AIDL provider: " + instance);
- providers.add(provider);
- }
-
- return providers;
- }
-
@Override
public void onStart() {
publishBinderService(Context.FINGERPRINT_SERVICE, mServiceWrapper);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
index bd21cf4..6d1715f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
@@ -25,7 +25,6 @@
import android.hardware.keymaster.HardwareAuthToken;
import android.util.Slog;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AcquisitionClient;
@@ -53,16 +52,6 @@
/**
* Interface to send results to the AidlResponseHandler's owner.
*/
- public interface HardwareUnavailableCallback {
- /**
- * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
- */
- void onHardwareUnavailable();
- }
-
- /**
- * Interface to send results to the AidlResponseHandler's owner.
- */
public interface AidlResponseHandlerCallback {
/**
* Invoked when enrollment is successful.
@@ -90,8 +79,6 @@
@NonNull
private final AuthSessionCoordinator mAuthSessionCoordinator;
@NonNull
- private final HardwareUnavailableCallback mHardwareUnavailableCallback;
- @NonNull
private final AidlResponseHandlerCallback mAidlResponseHandlerCallback;
public AidlResponseHandler(@NonNull Context context,
@@ -99,24 +86,6 @@
@NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull AuthSessionCoordinator authSessionCoordinator,
- @NonNull HardwareUnavailableCallback hardwareUnavailableCallback) {
- this(context, scheduler, sensorId, userId, lockoutTracker, lockoutResetDispatcher,
- authSessionCoordinator, hardwareUnavailableCallback,
- new AidlResponseHandlerCallback() {
- @Override
- public void onEnrollSuccess() {}
-
- @Override
- public void onHardwareUnavailable() {}
- });
- }
-
- public AidlResponseHandler(@NonNull Context context,
- @NonNull BiometricScheduler scheduler, int sensorId, int userId,
- @NonNull LockoutTracker lockoutTracker,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull AuthSessionCoordinator authSessionCoordinator,
- @NonNull HardwareUnavailableCallback hardwareUnavailableCallback,
@NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback) {
mContext = context;
mScheduler = scheduler;
@@ -125,7 +94,6 @@
mLockoutTracker = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
mAuthSessionCoordinator = authSessionCoordinator;
- mHardwareUnavailableCallback = hardwareUnavailableCallback;
mAidlResponseHandlerCallback = aidlResponseHandlerCallback;
}
@@ -171,11 +139,7 @@
handleResponse(ErrorConsumer.class, (c) -> {
c.onError(error, vendorCode);
if (error == Error.HW_UNAVAILABLE) {
- if (Flags.deHidl()) {
- mAidlResponseHandlerCallback.onHardwareUnavailable();
- } else {
- mHardwareUnavailableCallback.onHardwareUnavailable();
- }
+ mAidlResponseHandlerCallback.onHardwareUnavailable();
}
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 93d1b6e..5edcbed 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -31,23 +31,16 @@
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationState;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.Build;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Slog;
-import com.android.internal.R;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
@@ -67,7 +60,6 @@
import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
-import java.time.Clock;
import java.util.ArrayList;
import java.util.function.Supplier;
@@ -79,30 +71,17 @@
extends AuthenticationClient<AidlSession, FingerprintAuthenticateOptions>
implements Udfps, LockoutConsumer, PowerPressHandler {
private static final String TAG = "FingerprintAuthenticationClient";
- private static final int MESSAGE_AUTH_SUCCESS = 2;
- private static final int MESSAGE_FINGER_UP = 3;
@NonNull
private final SensorOverlays mSensorOverlays;
@NonNull
private final FingerprintSensorPropertiesInternal mSensorProps;
@NonNull
private final CallbackWithProbe<Probe> mALSProbeCallback;
- private final Handler mHandler;
- private final int mSkipWaitForPowerAcquireMessage;
- private final int mSkipWaitForPowerVendorAcquireMessage;
- private final long mFingerUpIgnoresPower = 500;
private final AuthSessionCoordinator mAuthSessionCoordinator;
@NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
@Nullable
private ICancellationSignal mCancellationSignal;
private boolean mIsPointerDown;
- private long mWaitForAuthKeyguard;
- private long mWaitForAuthBp;
- private long mIgnoreAuthFor;
- private long mSideFpsLastAcquireStartTime;
- private Runnable mAuthSuccessRunnable;
- private final Clock mClock;
-
public FingerprintAuthenticationClient(
@NonNull Context context,
@@ -125,9 +104,7 @@
@NonNull AuthenticationStateListeners authenticationStateListeners,
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
- @NonNull Handler handler,
@Authenticators.Types int biometricStrength,
- @NonNull Clock clock,
@Nullable LockoutTracker lockoutTracker) {
super(
context,
@@ -156,39 +133,7 @@
mAuthenticationStateListeners = authenticationStateListeners;
mSensorProps = sensorProps;
mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
- mHandler = handler;
-
- mWaitForAuthKeyguard =
- context.getResources()
- .getInteger(R.integer.config_sidefpsKeyguardPowerPressWindow);
- mWaitForAuthBp =
- context.getResources().getInteger(R.integer.config_sidefpsBpPowerPressWindow);
- mIgnoreAuthFor =
- context.getResources().getInteger(R.integer.config_sidefpsPostAuthDowntime);
-
- mSkipWaitForPowerAcquireMessage =
- context.getResources().getInteger(
- R.integer.config_sidefpsSkipWaitForPowerAcquireMessage);
- mSkipWaitForPowerVendorAcquireMessage =
- context.getResources().getInteger(
- R.integer.config_sidefpsSkipWaitForPowerVendorAcquireMessage);
mAuthSessionCoordinator = biometricContext.getAuthSessionCoordinator();
- mSideFpsLastAcquireStartTime = -1;
- mClock = clock;
-
- if (mSensorProps.isAnySidefpsType()) {
- if (Build.isDebuggable()) {
- mWaitForAuthKeyguard = Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.FINGERPRINT_SIDE_FPS_KG_POWER_WINDOW,
- (int) mWaitForAuthKeyguard, UserHandle.USER_CURRENT);
- mWaitForAuthBp = Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.FINGERPRINT_SIDE_FPS_BP_POWER_WINDOW, (int) mWaitForAuthBp,
- UserHandle.USER_CURRENT);
- mIgnoreAuthFor = Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME, (int) mIgnoreAuthFor,
- UserHandle.USER_CURRENT);
- }
- }
}
@Override
@@ -316,11 +261,7 @@
}
try {
- if (Flags.deHidl()) {
- startAuthentication();
- } else {
- mCancellationSignal = doAuthenticate();
- }
+ doAuthenticate();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
onError(
@@ -334,49 +275,7 @@
}
}
- private ICancellationSignal doAuthenticate() throws RemoteException {
- final AidlSession session = getFreshDaemon();
-
- final OperationContextExt opContext = getOperationContext();
- final ICancellationSignal cancel;
- if (session.hasContextMethods()) {
- cancel = session.getSession().authenticateWithContext(
- mOperationId, opContext.toAidlContext(getOptions()));
- } else {
- cancel = session.getSession().authenticate(mOperationId);
- }
-
- getBiometricContext().subscribe(opContext, ctx -> {
- if (session.hasContextMethods()) {
- try {
- session.getSession().onContextChanged(ctx);
- // TODO(b/317414324): Deprecate setIgnoreDisplayTouches
- if (ctx.operationState != null && ctx.operationState.getTag()
- == OperationState.fingerprintOperationState) {
- session.getSession().setIgnoreDisplayTouches(
- ctx.operationState.getFingerprintOperationState().isHardwareIgnoringTouches);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify context changed", e);
- }
- }
-
- // TODO(b/243836005): this should come via ctx
- final boolean isAwake = getBiometricContext().isAwake();
- if (isAwake) {
- mALSProbeCallback.getProbe().enable();
- } else {
- mALSProbeCallback.getProbe().disable();
- }
- });
- if (getBiometricContext().isAwake()) {
- mALSProbeCallback.getProbe().enable();
- }
-
- return cancel;
- }
-
- private void startAuthentication() {
+ private void doAuthenticate() throws RemoteException {
final AidlSession session = getFreshDaemon();
final OperationContextExt opContext = getOperationContext();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 8d2b46f..1db2fad 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.hardware.biometrics.BiometricRequestConstants;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationState;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
@@ -31,7 +30,6 @@
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.OperationContextExt;
@@ -106,13 +104,8 @@
resetIgnoreDisplayTouches();
mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD,
this);
-
try {
- if (Flags.deHidl()) {
- startDetectInteraction();
- } else {
- mCancellationSignal = doDetectInteraction();
- }
+ doDetectInteraction();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting finger detect", e);
mSensorOverlays.hide(getSensorId());
@@ -120,33 +113,7 @@
}
}
- private ICancellationSignal doDetectInteraction() throws RemoteException {
- final AidlSession session = getFreshDaemon();
-
- if (session.hasContextMethods()) {
- final OperationContextExt opContext = getOperationContext();
- final ICancellationSignal cancel = session.getSession().detectInteractionWithContext(
- opContext.toAidlContext(mOptions));
- getBiometricContext().subscribe(opContext, ctx -> {
- try {
- session.getSession().onContextChanged(ctx);
- // TODO(b/317414324): Deprecate setIgnoreDisplayTouches
- if (ctx.operationState != null && ctx.operationState.getTag()
- == OperationState.fingerprintOperationState) {
- session.getSession().setIgnoreDisplayTouches(
- ctx.operationState.getFingerprintOperationState().isHardwareIgnoringTouches);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify context changed", e);
- }
- });
- return cancel;
- } else {
- return session.getSession().detectInteraction();
- }
- }
-
- private void startDetectInteraction() throws RemoteException {
+ private void doDetectInteraction() throws RemoteException {
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index a24ab1d..86ebabe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -26,7 +26,6 @@
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationState;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintEnrollOptions;
@@ -40,7 +39,6 @@
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -209,11 +207,7 @@
BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
try {
- if (Flags.deHidl()) {
- startEnroll();
- } else {
- mCancellationSignal = doEnroll();
- }
+ doEnroll();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS,
@@ -222,35 +216,7 @@
}
}
- private ICancellationSignal doEnroll() throws RemoteException {
- final AidlSession session = getFreshDaemon();
- final HardwareAuthToken hat =
- HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken);
-
- if (session.hasContextMethods()) {
- final OperationContextExt opContext = getOperationContext();
- final ICancellationSignal cancel = session.getSession().enrollWithContext(
- hat, opContext.toAidlContext());
- getBiometricContext().subscribe(opContext, ctx -> {
- try {
- session.getSession().onContextChanged(ctx);
- // TODO(b/317414324): Deprecate setIgnoreDisplayTouches
- if (ctx.operationState != null && ctx.operationState.getTag()
- == OperationState.fingerprintOperationState) {
- session.getSession().setIgnoreDisplayTouches(
- ctx.operationState.getFingerprintOperationState().isHardwareIgnoringTouches);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify context changed", e);
- }
- });
- return cancel;
- } else {
- return session.getSession().enroll(hat);
- }
- }
-
- private void startEnroll() throws RemoteException {
+ private void doEnroll() throws RemoteException {
final AidlSession session = getFreshDaemon();
final HardwareAuthToken hat =
HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 9290f8a..beb3f2f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -29,12 +29,10 @@
import android.content.res.TypedArray;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.SensorLocationInternal;
-import android.hardware.biometrics.common.ComponentInfo;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.SensorProps;
@@ -50,10 +48,8 @@
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
@@ -63,7 +59,6 @@
import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.BiometricHandlerProvider;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -96,10 +91,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
-import java.util.stream.Collectors;
/**
* Provider for a single instance of the {@link IFingerprint} HAL.
@@ -200,11 +193,7 @@
mAuthenticationStateListeners = authenticationStateListeners;
mHalInstanceName = halInstanceName;
mFingerprintSensors = new SensorList<>(ActivityManager.getService());
- if (Flags.deHidl()) {
- mHandler = biometricHandlerProvider.getFingerprintHandler();
- } else {
- mHandler = new Handler(Looper.getMainLooper());
- }
+ mHandler = biometricHandlerProvider.getFingerprintHandler();
mLockoutResetDispatcher = lockoutResetDispatcher;
mActivityTaskManager = ActivityTaskManager.getInstance();
mTaskStackListener = new BiometricTaskStackListener();
@@ -230,66 +219,19 @@
private void initSensors(boolean resetLockoutRequiresHardwareAuthToken, SensorProps[] props,
GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- if (Flags.deHidl()) {
- if (!resetLockoutRequiresHardwareAuthToken) {
- Slog.d(getTag(), "Adding HIDL configs");
- for (SensorProps sensorConfig: props) {
- addHidlSensors(sensorConfig, gestureAvailabilityDispatcher,
- resetLockoutRequiresHardwareAuthToken);
- }
- } else {
- Slog.d(getTag(), "Adding AIDL configs");
- final List<SensorLocationInternal> workaroundLocations =
- getWorkaroundSensorProps(mContext);
- for (SensorProps prop : props) {
- addAidlSensors(prop, gestureAvailabilityDispatcher, workaroundLocations,
- resetLockoutRequiresHardwareAuthToken);
- }
+ if (!resetLockoutRequiresHardwareAuthToken) {
+ Slog.d(getTag(), "Adding HIDL configs");
+ for (SensorProps sensorConfig: props) {
+ addHidlSensors(sensorConfig, gestureAvailabilityDispatcher,
+ resetLockoutRequiresHardwareAuthToken);
}
} else {
+ Slog.d(getTag(), "Adding AIDL configs");
final List<SensorLocationInternal> workaroundLocations =
getWorkaroundSensorProps(mContext);
-
for (SensorProps prop : props) {
- final int sensorId = prop.commonProps.sensorId;
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- if (prop.commonProps.componentInfo != null) {
- for (ComponentInfo info : prop.commonProps.componentInfo) {
- componentInfo.add(new ComponentInfoInternal(info.componentId,
- info.hardwareVersion, info.firmwareVersion, info.serialNumber,
- info.softwareVersion));
- }
- }
- final FingerprintSensorPropertiesInternal internalProp =
- new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId,
- prop.commonProps.sensorStrength,
- prop.commonProps.maxEnrollmentsPerUser,
- componentInfo,
- prop.sensorType,
- prop.halControlsIllumination,
- true /* resetLockoutRequiresHardwareAuthToken */,
- !workaroundLocations.isEmpty() ? workaroundLocations :
- Arrays.stream(prop.sensorLocations).map(
- location -> new SensorLocationInternal(
- location.display,
- location.sensorLocationX,
- location.sensorLocationY,
- location.sensorRadius))
- .collect(Collectors.toList()));
- final Sensor sensor = new Sensor(this, mContext, mHandler, internalProp,
- mBiometricContext);
- sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher);
- final int sessionUserId =
- sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
- sensor.getLazySession().get().getUserId();
- mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId,
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) {
- scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
- }
- });
- Slog.d(getTag(), "Added: " + mFingerprintSensors.get(sensorId).toString());
+ addAidlSensors(prop, gestureAvailabilityDispatcher, workaroundLocations,
+ resetLockoutRequiresHardwareAuthToken);
}
}
}
@@ -546,23 +488,7 @@
mFingerprintSensors.get(sensorId).getSensorProperties(),
mUdfpsOverlayController, mSidefpsController,
mAuthenticationStateListeners, maxTemplatesPerUser, enrollReason, options);
- if (Flags.deHidl()) {
- scheduleForSensor(sensorId, client, mBiometricStateCallback);
- } else {
- scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
- mBiometricStateCallback, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- ClientMonitorCallback.super.onClientFinished(
- clientMonitor, success);
- if (success) {
- scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
- scheduleInvalidationRequest(sensorId, userId);
- }
- }
- }));
- }
+ scheduleForSensor(sensorId, client, mBiometricStateCallback);
});
return id;
}
@@ -605,13 +531,8 @@
final int userId = options.getUserId();
final int sensorId = options.getSensorId();
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
- final LockoutTracker lockoutTracker;
- if (Flags.deHidl()) {
- lockoutTracker = mFingerprintSensors.get(sensorId)
- .getLockoutTracker(true /* forAuth */);
- } else {
- lockoutTracker = null;
- }
+ final LockoutTracker lockoutTracker = mFingerprintSensors.get(sensorId)
+ .getLockoutTracker(true /* forAuth */);
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
mContext, mFingerprintSensors.get(sensorId).getLazySession(), token, requestId,
callback, operationId, restricted, options, cookie,
@@ -622,22 +543,16 @@
mTaskStackListener,
mUdfpsOverlayController, mSidefpsController,
mAuthenticationStateListeners, allowBackgroundAuthentication,
- mFingerprintSensors.get(sensorId).getSensorProperties(), mHandler,
+ mFingerprintSensors.get(sensorId).getSensorProperties(),
Utils.getCurrentStrength(sensorId),
- SystemClock.elapsedRealtimeClock(),
lockoutTracker);
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
mBiometricStateCallback.onClientStarted(clientMonitor);
- if (Flags.deHidl()) {
- mBiometricHandlerProvider.getBiometricCallbackHandler().post(() ->
- mAuthSessionCoordinator.authStartedFor(userId, sensorId,
- requestId));
- } else {
- mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId);
- }
+ mBiometricHandlerProvider.getBiometricCallbackHandler().post(() ->
+ mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId));
}
@Override
@@ -649,15 +564,10 @@
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
mBiometricStateCallback.onClientFinished(clientMonitor, success);
- if (Flags.deHidl()) {
- mBiometricHandlerProvider.getBiometricCallbackHandler().post(() ->
- mAuthSessionCoordinator.authEndedFor(userId,
- Utils.getCurrentStrength(sensorId), sensorId, requestId,
- success));
- } else {
- mAuthSessionCoordinator.authEndedFor(userId,
- Utils.getCurrentStrength(sensorId), sensorId, requestId, success);
- }
+ mBiometricHandlerProvider.getBiometricCallbackHandler().post(() ->
+ mAuthSessionCoordinator.authEndedFor(userId,
+ Utils.getCurrentStrength(sensorId), sensorId, requestId,
+ success));
}
});
@@ -764,10 +674,7 @@
@Override
public boolean isHardwareDetected(int sensorId) {
- if (Flags.deHidl()) {
- return mFingerprintSensors.get(sensorId).isHardwareDetected(mHalInstanceName);
- }
- return hasHalInstance();
+ return mFingerprintSensors.get(sensorId).isHardwareDetected(mHalInstanceName);
}
@Override
@@ -805,12 +712,7 @@
@Override
public int getLockoutModeForUser(int sensorId, int userId) {
- if (Flags.deHidl()) {
- return mFingerprintSensors.get(sensorId).getLockoutModeForUser(userId);
- } else {
- return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
- Utils.getCurrentStrength(sensorId));
- }
+ return mFingerprintSensors.get(sensorId).getLockoutModeForUser(userId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index af88c62..b7e3f70 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -42,7 +42,6 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -58,7 +57,6 @@
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
-import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
import com.android.server.biometrics.sensors.UserSwitchProvider;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
@@ -110,13 +108,6 @@
}
Sensor(@NonNull FingerprintProvider provider, @NonNull Context context,
- @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
- @NonNull BiometricContext biometricContext) {
- this(provider, context, handler, sensorProperties,
- biometricContext, null);
- }
-
- Sensor(@NonNull FingerprintProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull SensorProps sensorProp,
@NonNull BiometricContext biometricContext,
@NonNull List<SensorLocationInternal> workaroundLocation,
@@ -131,13 +122,8 @@
*/
public void init(@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
- if (Flags.deHidl()) {
- setScheduler(getBiometricSchedulerForInit(gestureAvailabilityDispatcher,
- lockoutResetDispatcher));
- } else {
- setScheduler(getUserAwareBiometricSchedulerForInit(gestureAvailabilityDispatcher,
- lockoutResetDispatcher));
- }
+ setScheduler(getBiometricSchedulerForInit(gestureAvailabilityDispatcher,
+ lockoutResetDispatcher));
mLockoutTracker = new LockoutCache();
mLazySession = () -> mCurrentSession != null ? mCurrentSession : null;
}
@@ -168,7 +154,7 @@
final AidlResponseHandler resultController = new AidlResponseHandler(
mContext, mScheduler, sensorId, newUserId,
mLockoutTracker, lockoutResetDispatcher,
- mBiometricContext.getAuthSessionCoordinator(), () -> {},
+ mBiometricContext.getAuthSessionCoordinator(),
new AidlResponseHandler.AidlResponseHandlerCallback() {
@Override
public void onEnrollSuccess() {
@@ -192,45 +178,6 @@
});
}
- private UserAwareBiometricScheduler<ISession, AidlSession>
- getUserAwareBiometricSchedulerForInit(
- GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- LockoutResetDispatcher lockoutResetDispatcher) {
- return new UserAwareBiometricScheduler<>(TAG,
- BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties),
- gestureAvailabilityDispatcher,
- () -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL,
- new UserAwareBiometricScheduler.UserSwitchCallback() {
- @NonNull
- @Override
- public StopUserClient<ISession> getStopUserClient(int userId) {
- return new FingerprintStopUserClient(mContext,
- () -> mLazySession.get().getSession(), mToken,
- userId, mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), mBiometricContext,
- () -> mCurrentSession = null);
- }
-
- @NonNull
- @Override
- public StartUserClient<IFingerprint, ISession> getStartUserClient(
- int newUserId) {
- final int sensorId = mSensorProperties.sensorId;
-
- final AidlResponseHandler resultController = new AidlResponseHandler(
- mContext, mScheduler, sensorId, newUserId,
- mLockoutTracker, lockoutResetDispatcher,
- mBiometricContext.getAuthSessionCoordinator(), () -> {
- Slog.e(TAG, "Fingerprint hardware unavailable.");
- mCurrentSession = null;
- });
-
- return Sensor.this.getStartUserClient(resultController, sensorId,
- newUserId);
- }
- });
- }
-
private FingerprintStartUserClient getStartUserClient(AidlResponseHandler resultController,
int sensorId, int newUserId) {
final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
deleted file mode 100644
index fc037ae..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ /dev/null
@@ -1,246 +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.biometrics.sensors.fingerprint.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.ITestSession;
-import android.hardware.biometrics.ITestSessionCallback;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.FingerprintEnrollOptions;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.sensors.BaseClientMonitor;
-import com.android.server.biometrics.sensors.BiometricStateCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-
-/**
- * A test session implementation for the {@link Fingerprint21} provider. See
- * {@link android.hardware.biometrics.BiometricTestSession}.
- */
-public class BiometricTestSessionImpl extends ITestSession.Stub {
-
- private static final String TAG = "BiometricTestSessionImpl";
-
- @NonNull private final Context mContext;
- private final int mSensorId;
- @NonNull private final ITestSessionCallback mCallback;
- @NonNull private final BiometricStateCallback mBiometricStateCallback;
- @NonNull private final Fingerprint21 mFingerprint21;
- @NonNull private final Fingerprint21.HalResultController mHalResultController;
- @NonNull private final Set<Integer> mEnrollmentIds;
- @NonNull private final Random mRandom;
-
- /**
- * Internal receiver currently only used for enroll. Results do not need to be forwarded to the
- * test, since enrollment is a platform-only API. The authentication path is tested through
- * the public FingerprintManager APIs and does not use this receiver.
- */
- private final IFingerprintServiceReceiver mReceiver = new IFingerprintServiceReceiver.Stub() {
- @Override
- public void onEnrollResult(Fingerprint fp, int remaining) {
-
- }
-
- @Override
- public void onAcquired(int acquiredInfo, int vendorCode) {
-
- }
-
- @Override
- public void onAuthenticationSucceeded(Fingerprint fp, int userId,
- boolean isStrongBiometric) {
-
- }
-
- @Override
- public void onFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) {
-
- }
-
- @Override
- public void onAuthenticationFailed() {
-
- }
-
- @Override
- public void onError(int error, int vendorCode) {
-
- }
-
- @Override
- public void onRemoved(Fingerprint fp, int remaining) {
-
- }
-
- @Override
- public void onChallengeGenerated(int sensorId, int userId, long challenge) {
-
- }
-
- @Override
- public void onUdfpsPointerDown(int sensorId) {
-
- }
-
- @Override
- public void onUdfpsPointerUp(int sensorId) {
-
- }
-
- @Override
- public void onUdfpsOverlayShown() {
-
- }
- };
-
- BiometricTestSessionImpl(@NonNull Context context, int sensorId,
- @NonNull ITestSessionCallback callback,
- @NonNull BiometricStateCallback biometricStateCallback,
- @NonNull Fingerprint21 fingerprint21,
- @NonNull Fingerprint21.HalResultController halResultController) {
- mContext = context;
- mSensorId = sensorId;
- mCallback = callback;
- mFingerprint21 = fingerprint21;
- mBiometricStateCallback = biometricStateCallback;
- mHalResultController = halResultController;
- mEnrollmentIds = new HashSet<>();
- mRandom = new Random();
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void setTestHalEnabled(boolean enabled) {
-
- super.setTestHalEnabled_enforcePermission();
-
- mFingerprint21.setTestHalEnabled(enabled);
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void startEnroll(int userId) {
-
- super.startEnroll_enforcePermission();
-
- mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL,
- (new FingerprintEnrollOptions.Builder()).build());
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void finishEnroll(int userId) {
-
- super.finishEnroll_enforcePermission();
-
- int nextRandomId = mRandom.nextInt();
- while (mEnrollmentIds.contains(nextRandomId)) {
- nextRandomId = mRandom.nextInt();
- }
-
- mEnrollmentIds.add(nextRandomId);
- mHalResultController.onEnrollResult(0 /* deviceId */,
- nextRandomId /* fingerId */, userId, 0);
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void acceptAuthentication(int userId) {
-
- // Fake authentication with any of the existing fingers
- super.acceptAuthentication_enforcePermission();
-
- List<Fingerprint> fingerprints = FingerprintUtils.getLegacyInstance(mSensorId)
- .getBiometricsForUser(mContext, userId);
- if (fingerprints.isEmpty()) {
- Slog.w(TAG, "No fingerprints, returning");
- return;
- }
- final int fid = fingerprints.get(0).getBiometricId();
- final ArrayList<Byte> hat = new ArrayList<>(Collections.nCopies(69, (byte) 0));
- mHalResultController.onAuthenticated(0 /* deviceId */, fid, userId, hat);
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void rejectAuthentication(int userId) {
-
- super.rejectAuthentication_enforcePermission();
-
- mHalResultController.onAuthenticated(0 /* deviceId */, 0 /* fingerId */, userId, null);
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void notifyAcquired(int userId, int acquireInfo) {
-
- super.notifyAcquired_enforcePermission();
-
- mHalResultController.onAcquired(0 /* deviceId */, acquireInfo, 0 /* vendorCode */);
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void notifyError(int userId, int errorCode) {
-
- super.notifyError_enforcePermission();
-
- mHalResultController.onError(0 /* deviceId */, errorCode, 0 /* vendorCode */);
- }
-
- @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
- @Override
- public void cleanupInternalState(int userId) {
-
- super.cleanupInternalState_enforcePermission();
-
- mFingerprint21.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- try {
- mCallback.onCleanupStarted(clientMonitor.getTargetUserId());
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
-
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- try {
- mCallback.onCleanupFinished(clientMonitor.getTargetUserId());
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
- });
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
deleted file mode 100644
index 33e448b..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ /dev/null
@@ -1,1289 +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.biometrics.sensors.fingerprint.hidl;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.SynchronousUserSwitchObserver;
-import android.app.TaskStackListener;
-import android.app.UserSwitchObserver;
-import android.content.Context;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.biometrics.IInvalidationCallback;
-import android.hardware.biometrics.ITestSession;
-import android.hardware.biometrics.ITestSessionCallback;
-import android.hardware.biometrics.fingerprint.PointerContext;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.FingerprintAuthenticateOptions;
-import android.hardware.fingerprint.FingerprintEnrollOptions;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.FingerprintSensorProperties;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.hardware.fingerprint.ISidefpsController;
-import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IHwBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
-import com.android.server.biometrics.AuthenticationStatsCollector;
-import com.android.server.biometrics.Flags;
-import com.android.server.biometrics.SensorServiceStateProto;
-import com.android.server.biometrics.SensorStateProto;
-import com.android.server.biometrics.UserStateProto;
-import com.android.server.biometrics.Utils;
-import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
-import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto;
-import com.android.server.biometrics.fingerprint.PerformanceStatsProto;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.AcquisitionClient;
-import com.android.server.biometrics.sensors.AuthSessionCoordinator;
-import com.android.server.biometrics.sensors.AuthenticationClient;
-import com.android.server.biometrics.sensors.AuthenticationConsumer;
-import com.android.server.biometrics.sensors.AuthenticationStateListeners;
-import com.android.server.biometrics.sensors.BaseClientMonitor;
-import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.BiometricStateCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
-import com.android.server.biometrics.sensors.EnumerateConsumer;
-import com.android.server.biometrics.sensors.ErrorConsumer;
-import com.android.server.biometrics.sensors.LockoutCache;
-import com.android.server.biometrics.sensors.LockoutResetDispatcher;
-import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.PerformanceTracker;
-import com.android.server.biometrics.sensors.RemovalConsumer;
-import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
-import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
-import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
-import com.android.server.biometrics.sensors.fingerprint.Udfps;
-import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
-import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.function.Supplier;
-
-/**
- * Supports a single instance of the {@link android.hardware.biometrics.fingerprint.V2_1} or
- * its extended minor versions.
- */
-public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider {
-
- private static final String TAG = "Fingerprint21";
- private static final int ENROLL_TIMEOUT_SEC = 60;
-
- private boolean mTestHalEnabled;
-
- final Context mContext;
- @NonNull private final BiometricStateCallback mBiometricStateCallback;
- @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
- private final ActivityTaskManager mActivityTaskManager;
- @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
- private final BiometricScheduler<IBiometricsFingerprint, AidlSession> mScheduler;
- private final Handler mHandler;
- private final LockoutResetDispatcher mLockoutResetDispatcher;
- private final LockoutFrameworkImpl mLockoutTracker;
- private final BiometricTaskStackListener mTaskStackListener;
- private final Supplier<IBiometricsFingerprint> mLazyDaemon;
- private final Map<Integer, Long> mAuthenticatorIds;
-
- @Nullable private IBiometricsFingerprint mDaemon;
- @NonNull private final HalResultController mHalResultController;
- @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
-
- // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
- @Nullable private ISidefpsController mSidefpsController;
- @NonNull private final BiometricContext mBiometricContext;
- @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector;
- // for requests that do not use biometric prompt
- @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
- private int mCurrentUserId = UserHandle.USER_NULL;
- private final boolean mIsUdfps;
- private final int mSensorId;
- private final boolean mIsPowerbuttonFps;
- private AidlSession mSession;
-
- private final class BiometricTaskStackListener extends TaskStackListener {
- @Override
- public void onTaskStackChanged() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationClient)) {
- Slog.e(TAG, "Task stack changed for client: " + client);
- return;
- }
- if (Utils.isKeyguard(mContext, client.getOwnerString())
- || Utils.isSystem(mContext, client.getOwnerString())) {
- return; // Keyguard is always allowed
- }
-
- if (Utils.isBackground(client.getOwnerString())
- && !client.isAlreadyDone()) {
- Slog.e(TAG, "Stopping background authentication,"
- + " currentClient: " + client);
- mScheduler.cancelAuthenticationOrDetection(
- client.getToken(), client.getRequestId());
- }
- });
- }
- }
-
- private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback =
- new LockoutFrameworkImpl.LockoutResetCallback() {
- @Override
- public void onLockoutReset(int userId) {
- mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorProperties.sensorId);
- }
- };
-
- private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) {
- scheduleInternalCleanup(newUserId, null /* callback */);
- }
- };
-
- public static class HalResultController extends IBiometricsFingerprintClientCallback.Stub {
-
- /**
- * Interface to sends results to the HalResultController's owner.
- */
- public interface Callback {
- /**
- * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
- */
- void onHardwareUnavailable();
- }
-
- private final int mSensorId;
- @NonNull private final Context mContext;
- @NonNull final Handler mHandler;
- @NonNull final BiometricScheduler<IBiometricsFingerprint, AidlSession> mScheduler;
- @Nullable private Callback mCallback;
-
- HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
- @NonNull BiometricScheduler<IBiometricsFingerprint, AidlSession> scheduler) {
- mSensorId = sensorId;
- mContext = context;
- mHandler = handler;
- mScheduler = scheduler;
- }
-
- public void setCallback(@Nullable Callback callback) {
- mCallback = callback;
- }
-
- @Override
- public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintEnrollClient)) {
- Slog.e(TAG, "onEnrollResult for non-enroll client: "
- + Utils.getClientName(client));
- return;
- }
-
- final int currentUserId = client.getTargetUserId();
- final CharSequence name = FingerprintUtils.getLegacyInstance(mSensorId)
- .getUniqueName(mContext, currentUserId);
- final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId);
-
- final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
- enrollClient.onEnrollResult(fingerprint, remaining);
- });
- }
-
- @Override
- public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) {
- onAcquired_2_2(deviceId, acquiredInfo, vendorCode);
- }
-
- @Override
- public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AcquisitionClient)) {
- Slog.e(TAG, "onAcquired for non-acquisition client: "
- + Utils.getClientName(client));
- return;
- }
-
- final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(acquiredInfo, vendorCode);
- });
- }
-
- @Override
- public void onAuthenticated(long deviceId, int fingerId, int groupId,
- ArrayList<Byte> token) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(TAG, "onAuthenticated for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final boolean authenticated = fingerId != 0;
- final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
- authenticationConsumer.onAuthenticated(fp, authenticated, token);
- });
- }
-
- @Override
- public void onError(long deviceId, int error, int vendorCode) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- Slog.d(TAG, "handleError"
- + ", client: " + Utils.getClientName(client)
- + ", error: " + error
- + ", vendorCode: " + vendorCode);
- if (!(client instanceof ErrorConsumer)) {
- Slog.e(TAG, "onError for non-error consumer: " + Utils.getClientName(client));
- return;
- }
-
- final ErrorConsumer errorConsumer = (ErrorConsumer) client;
- errorConsumer.onError(error, vendorCode);
-
- if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
- Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE");
- if (mCallback != null) {
- mCallback.onHardwareUnavailable();
- }
- }
- });
- }
-
- @Override
- public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
- mHandler.post(() -> {
- Slog.d(TAG, "Removed, fingerId: " + fingerId + ", remaining: " + remaining);
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof RemovalConsumer)) {
- Slog.e(TAG, "onRemoved for non-removal consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
- final RemovalConsumer removalConsumer = (RemovalConsumer) client;
- removalConsumer.onRemoved(fp, remaining);
- });
- }
-
- @Override
- public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof EnumerateConsumer)) {
- Slog.e(TAG, "onEnumerate for non-enumerate consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
- final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
- enumerateConsumer.onEnumerationResult(fp, remaining);
- });
- }
- }
-
- @VisibleForTesting
- Fingerprint21(@NonNull Context context,
- @NonNull BiometricStateCallback biometricStateCallback,
- @NonNull AuthenticationStateListeners authenticationStateListeners,
- @NonNull FingerprintSensorPropertiesInternal sensorProps,
- @NonNull BiometricScheduler<IBiometricsFingerprint, AidlSession> scheduler,
- @NonNull Handler handler,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull HalResultController controller,
- @NonNull BiometricContext biometricContext) {
- mContext = context;
- mBiometricStateCallback = biometricStateCallback;
- mAuthenticationStateListeners = authenticationStateListeners;
- mBiometricContext = biometricContext;
-
- mSensorProperties = sensorProps;
- mSensorId = sensorProps.sensorId;
- mIsUdfps = sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
- || sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
- mIsPowerbuttonFps = sensorProps.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON;
-
- mScheduler = scheduler;
- mHandler = handler;
- mActivityTaskManager = ActivityTaskManager.getInstance();
- mTaskStackListener = new BiometricTaskStackListener();
- mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>());
- mLazyDaemon = Fingerprint21.this::getDaemon;
- mLockoutResetDispatcher = lockoutResetDispatcher;
- mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback);
- mHalResultController = controller;
- mHalResultController.setCallback(() -> {
- mDaemon = null;
- mCurrentUserId = UserHandle.USER_NULL;
- });
-
- AuthenticationStatsBroadcastReceiver mBroadcastReceiver =
- new AuthenticationStatsBroadcastReceiver(
- mContext,
- BiometricsProtoEnums.MODALITY_FINGERPRINT,
- (AuthenticationStatsCollector collector) -> {
- Slog.d(TAG, "Initializing AuthenticationStatsCollector");
- mAuthenticationStatsCollector = collector;
- });
-
- try {
- ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to register user switch observer");
- }
- }
-
- public static Fingerprint21 newInstance(@NonNull Context context,
- @NonNull BiometricStateCallback biometricStateCallback,
- @NonNull AuthenticationStateListeners authenticationStateListeners,
- @NonNull FingerprintSensorPropertiesInternal sensorProps,
- @NonNull Handler handler,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- final BiometricScheduler<IBiometricsFingerprint, AidlSession> scheduler =
- new BiometricScheduler<>(
- BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps),
- gestureAvailabilityDispatcher);
- final HalResultController controller = new HalResultController(sensorProps.sensorId,
- context, handler, scheduler);
- return new Fingerprint21(context, biometricStateCallback, authenticationStateListeners,
- sensorProps, scheduler, handler, lockoutResetDispatcher, controller,
- BiometricContext.getInstance(context));
- }
-
- @Override
- public void serviceDied(long cookie) {
- Slog.e(TAG, "HAL died");
- mHandler.post(() -> {
- PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId)
- .incrementHALDeathCount();
- mDaemon = null;
- mCurrentUserId = UserHandle.USER_NULL;
-
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (client instanceof ErrorConsumer) {
- Slog.e(TAG, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
- final ErrorConsumer errorConsumer = (ErrorConsumer) client;
- errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
-
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- BiometricsProtoEnums.MODALITY_FINGERPRINT,
- BiometricsProtoEnums.ISSUE_HAL_DEATH,
- -1 /* sensorId */);
- }
-
- mScheduler.recordCrashState();
- mScheduler.reset();
- });
- }
-
- synchronized AidlSession getSession() {
- if (mDaemon != null && mSession != null) {
- return mSession;
- } else {
- return mSession = new AidlSession(this::getDaemon,
- mCurrentUserId, new AidlResponseHandler(mContext,
- mScheduler, mSensorId, mCurrentUserId, new LockoutCache(),
- mLockoutResetDispatcher, new AuthSessionCoordinator(), () -> {
- mDaemon = null;
- mCurrentUserId = UserHandle.USER_NULL;
- }));
- }
- }
-
- @VisibleForTesting
- synchronized IBiometricsFingerprint getDaemon() {
- if (mTestHalEnabled) {
- final TestHal testHal = new TestHal(mContext, mSensorId);
- testHal.setNotify(mHalResultController);
- return testHal;
- }
-
- if (mDaemon != null) {
- return mDaemon;
- }
-
- Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
- + mScheduler.getCurrentClient());
- try {
- mDaemon = IBiometricsFingerprint.getService();
- } catch (java.util.NoSuchElementException e) {
- // Service doesn't exist or cannot be opened.
- Slog.w(TAG, "NoSuchElementException", e);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get fingerprint HAL", e);
- }
-
- if (mDaemon == null) {
- Slog.w(TAG, "Fingerprint HAL not available");
- return null;
- }
-
- mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
-
- // HAL ID for these HIDL versions are only used to determine if callbacks have been
- // successfully set.
- long halId = 0;
- try {
- halId = mDaemon.setNotify(mHalResultController);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to set callback for fingerprint HAL", e);
- mDaemon = null;
- }
-
- Slog.d(TAG, "Fingerprint HAL ready, HAL ID: " + halId);
- if (halId != 0) {
- scheduleLoadAuthenticatorIds();
- scheduleInternalCleanup(ActivityManager.getCurrentUser(), null /* callback */);
- } else {
- Slog.e(TAG, "Unable to set callback");
- mDaemon = null;
- }
-
- return mDaemon;
- }
-
- @Nullable IUdfpsOverlayController getUdfpsOverlayController() {
- return mUdfpsOverlayController;
- }
-
- private void scheduleLoadAuthenticatorIds() {
- // Note that this can be performed on the scheduler (as opposed to being done immediately
- // when the HAL is (re)loaded, since
- // 1) If this is truly the first time it's being performed (e.g. system has just started),
- // this will be run very early and way before any applications need to generate keys.
- // 2) If this is being performed to refresh the authenticatorIds (e.g. HAL crashed and has
- // just been reloaded), the framework already has a cache of the authenticatorIds. This
- // is safe because authenticatorIds only change when A) new template has been enrolled,
- // or B) all templates are removed.
- mHandler.post(() -> {
- for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
- final int targetUserId = user.id;
- if (!mAuthenticatorIds.containsKey(targetUserId)) {
- scheduleUpdateActiveUserWithoutHandler(targetUserId, true /* force */);
- }
- }
- });
- }
-
- private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
- scheduleUpdateActiveUserWithoutHandler(targetUserId, false /* force */);
- }
-
- /**
- * Schedules the {@link FingerprintUpdateActiveUserClient} without posting the work onto the
- * handler. Many/most APIs are user-specific. However, the HAL requires explicit "setActiveUser"
- * invocation prior to authenticate/enroll/etc. Thus, internally we usually want to schedule
- * this operation on the same lambda/runnable as those operations so that the ordering is
- * correct.
- *
- * @param targetUserId Switch to this user, and update their authenticatorId
- * @param force Always retrieve the authenticatorId, even if we are already the targetUserId
- */
- private void scheduleUpdateActiveUserWithoutHandler(int targetUserId, boolean force) {
- final boolean hasEnrolled =
- !getEnrolledFingerprints(mSensorProperties.sensorId, targetUserId).isEmpty();
- final FingerprintUpdateActiveUserClientLegacy client =
- new FingerprintUpdateActiveUserClientLegacy(mContext, mLazyDaemon, targetUserId,
- mContext.getOpPackageName(), mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext,
- this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- if (success) {
- if (mCurrentUserId != targetUserId) {
- // Create new session with updated user ID
- mSession = null;
- }
- mCurrentUserId = targetUserId;
- } else {
- Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
- }
- }
- });
- }
-
- private int getCurrentUser() {
- return mCurrentUserId;
- }
-
- @Override
- public boolean containsSensor(int sensorId) {
- return mSensorProperties.sensorId == sensorId;
- }
-
- @Override
- @NonNull
- public List<FingerprintSensorPropertiesInternal> getSensorProperties() {
- final List<FingerprintSensorPropertiesInternal> properties = new ArrayList<>();
- properties.add(mSensorProperties);
- return properties;
- }
-
- @Nullable
- @Override
- public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId) {
- return mSensorProperties;
- }
-
- @Override
- public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
- // Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
- // thread.
- mHandler.post(() -> {
- if (Flags.deHidl()) {
- scheduleResetLockoutAidl(sensorId, userId, hardwareAuthToken);
- } else {
- scheduleResetLockoutHidl(sensorId, userId);
- }
- });
- }
-
- private void scheduleResetLockoutAidl(int sensorId, int userId,
- @Nullable byte[] hardwareAuthToken) {
- final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintResetLockoutClient client =
- new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintResetLockoutClient(
- mContext, this::getSession, userId, mContext.getOpPackageName(),
- sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext, hardwareAuthToken, mLockoutTracker,
- mLockoutResetDispatcher,
- Utils.getCurrentStrength(sensorId));
- mScheduler.scheduleClientMonitor(client);
- }
-
- private void scheduleResetLockoutHidl(int sensorId, int userId) {
- final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
- userId, mContext.getOpPackageName(), sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext, mLockoutTracker);
- mScheduler.scheduleClientMonitor(client);
- }
-
- @Override
- public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
- @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
- mHandler.post(() -> {
- if (Flags.deHidl()) {
- scheduleGenerateChallengeAidl(userId, token, receiver, opPackageName);
- } else {
- scheduleGenerateChallengeHidl(userId, token, receiver, opPackageName);
- }
- });
- }
-
- private void scheduleGenerateChallengeAidl(int userId, @NonNull IBinder token,
- @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
- final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintGenerateChallengeClient client =
- new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintGenerateChallengeClient(
- mContext, this::getSession, token,
- new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext);
- mScheduler.scheduleClientMonitor(client);
- }
-
- private void scheduleGenerateChallengeHidl(int userId, @NonNull IBinder token,
- @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
- final FingerprintGenerateChallengeClient client =
- new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext);
- mScheduler.scheduleClientMonitor(client);
- }
-
- @Override
- public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
- @NonNull String opPackageName, long challenge) {
- mHandler.post(() -> {
- if (Flags.deHidl()) {
- scheduleRevokeChallengeAidl(userId, token, opPackageName);
- } else {
- scheduleRevokeChallengeHidl(userId, token, opPackageName);
- }
- });
- }
-
- private void scheduleRevokeChallengeAidl(int userId, @NonNull IBinder token,
- @NonNull String opPackageName) {
- final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintRevokeChallengeClient client =
- new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintRevokeChallengeClient(
- mContext, this::getSession,
- token, userId, opPackageName,
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext, 0L);
- mScheduler.scheduleClientMonitor(client);
- }
-
- private void scheduleRevokeChallengeHidl(int userId, @NonNull IBinder token,
- @NonNull String opPackageName) {
- final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
- mContext, mLazyDaemon, token, userId, opPackageName,
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext);
- mScheduler.scheduleClientMonitor(client);
- }
-
- @Override
- public long scheduleEnroll(int sensorId, @NonNull IBinder token,
- @NonNull byte[] hardwareAuthToken, int userId,
- @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason,
- @NonNull FingerprintEnrollOptions options) {
- final long id = mRequestCounter.incrementAndGet();
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
-
- if (Flags.deHidl()) {
- scheduleEnrollAidl(token, hardwareAuthToken, userId, receiver,
- opPackageName, enrollReason, id, options);
- } else {
- scheduleEnrollHidl(token, hardwareAuthToken, userId, receiver,
- opPackageName, enrollReason, id, options);
- }
- });
- return id;
- }
-
- private void scheduleEnrollHidl(@NonNull IBinder token,
- @NonNull byte[] hardwareAuthToken, int userId,
- @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason, long id,
- @NonNull FingerprintEnrollOptions options) {
- final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
- mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
- userId, hardwareAuthToken, opPackageName,
- FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mUdfpsOverlayController,
- // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
- mSidefpsController,
- mAuthenticationStateListeners, enrollReason, options);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- mBiometricStateCallback.onClientStarted(clientMonitor);
- }
-
- @Override
- public void onBiometricAction(int action) {
- mBiometricStateCallback.onBiometricAction(action);
- }
-
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- mBiometricStateCallback.onClientFinished(clientMonitor, success);
- if (success) {
- // Update authenticatorIds
- scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
- true /* force */);
- }
- }
- });
- }
-
- private void scheduleEnrollAidl(@NonNull IBinder token,
- @NonNull byte[] hardwareAuthToken, int userId,
- @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason, long id,
- @NonNull FingerprintEnrollOptions options) {
- final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient
- client =
- new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient(
- mContext,
- this::getSession, token, id,
- new ClientMonitorCallbackConverter(receiver),
- userId, hardwareAuthToken, opPackageName,
- FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext, null /* sensorProps */,
- mUdfpsOverlayController,
- // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
- mSidefpsController,
- mAuthenticationStateListeners,
- mContext.getResources().getInteger(
- com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser),
- enrollReason, options);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- mBiometricStateCallback.onClientStarted(clientMonitor);
- }
-
- @Override
- public void onBiometricAction(int action) {
- mBiometricStateCallback.onBiometricAction(action);
- }
-
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- mBiometricStateCallback.onClientFinished(clientMonitor, success);
- if (success) {
- // Update authenticatorIds
- scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
- true /* force */);
- }
- }
- });
- }
-
- @Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
- mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
- }
-
- @Override
- public long scheduleFingerDetect(@NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener,
- @NonNull FingerprintAuthenticateOptions options,
- int statsClient) {
- final long id = mRequestCounter.incrementAndGet();
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(options.getUserId());
-
- final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
-
- if (Flags.deHidl()) {
- scheduleFingerDetectAidl(token, listener, options, statsClient, id,
- isStrongBiometric);
- } else {
- scheduleFingerDetectHidl(token, listener, options, statsClient, id,
- isStrongBiometric);
- }
- });
-
- return id;
- }
-
- private void scheduleFingerDetectHidl(@NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener,
- @NonNull FingerprintAuthenticateOptions options,
- int statsClient, long id, boolean isStrongBiometric) {
- final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
- mLazyDaemon, token, id, listener, options,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- mAuthenticationStatsCollector),
- mBiometricContext, mUdfpsOverlayController, isStrongBiometric);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
- }
-
- private void scheduleFingerDetectAidl(@NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener,
- @NonNull FingerprintAuthenticateOptions options,
- int statsClient, long id, boolean isStrongBiometric) {
- final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintDetectClient
- client =
- new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintDetectClient(
- mContext,
- this::getSession, token, id, listener, options,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- mAuthenticationStatsCollector),
- mBiometricContext, mUdfpsOverlayController, isStrongBiometric);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
- }
-
- @Override
- public void scheduleAuthenticate(@NonNull IBinder token, long operationId,
- int cookie, @NonNull ClientMonitorCallbackConverter listener,
- @NonNull FingerprintAuthenticateOptions options,
- long requestId, boolean restricted, int statsClient,
- boolean allowBackgroundAuthentication) {
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(options.getUserId());
-
- final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
-
- if (Flags.deHidl()) {
- scheduleAuthenticateAidl(token, operationId, cookie, listener, options, requestId,
- restricted, statsClient, allowBackgroundAuthentication, isStrongBiometric);
- } else {
- scheduleAuthenticateHidl(token, operationId, cookie, listener, options, requestId,
- restricted, statsClient, allowBackgroundAuthentication, isStrongBiometric);
- }
- });
- }
-
- private void scheduleAuthenticateAidl(@NonNull IBinder token, long operationId,
- int cookie, @NonNull ClientMonitorCallbackConverter listener,
- @NonNull FingerprintAuthenticateOptions options,
- long requestId, boolean restricted, int statsClient,
- boolean allowBackgroundAuthentication, boolean isStrongBiometric) {
- final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintAuthenticationClient
- client =
- new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintAuthenticationClient(
- mContext, this::getSession, token, requestId, listener, operationId,
- restricted, options, cookie, false /* requireConfirmation */,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- mAuthenticationStatsCollector),
- mBiometricContext, isStrongBiometric,
- mTaskStackListener,
- mUdfpsOverlayController, mSidefpsController,
- mAuthenticationStateListeners,
- allowBackgroundAuthentication, mSensorProperties, mHandler,
- Utils.getCurrentStrength(mSensorId), null /* clock */, mLockoutTracker);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
- }
-
- private void scheduleAuthenticateHidl(@NonNull IBinder token, long operationId,
- int cookie, @NonNull ClientMonitorCallbackConverter listener,
- @NonNull FingerprintAuthenticateOptions options,
- long requestId, boolean restricted, int statsClient,
- boolean allowBackgroundAuthentication, boolean isStrongBiometric) {
- final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
- mContext, mLazyDaemon, token, requestId, listener, operationId,
- restricted, options, cookie, false /* requireConfirmation */,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- mAuthenticationStatsCollector),
- mBiometricContext, isStrongBiometric,
- mTaskStackListener, mLockoutTracker,
- mUdfpsOverlayController, mSidefpsController,
- mAuthenticationStateListeners,
- allowBackgroundAuthentication, mSensorProperties,
- Utils.getCurrentStrength(mSensorId));
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
- }
-
- @Override
- public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
- int cookie, @NonNull ClientMonitorCallbackConverter listener,
- @NonNull FingerprintAuthenticateOptions options, boolean restricted, int statsClient,
- boolean allowBackgroundAuthentication) {
- final long id = mRequestCounter.incrementAndGet();
-
- scheduleAuthenticate(token, operationId, cookie, listener,
- options, id, restricted, statsClient, allowBackgroundAuthentication);
-
- return id;
- }
-
- @Override
- public void startPreparedClient(int sensorId, int cookie) {
- mHandler.post(() -> mScheduler.startPreparedClient(cookie));
- }
-
- @Override
- public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) {
- Slog.d(TAG, "cancelAuthentication, sensorId: " + sensorId);
- mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token, requestId));
- }
-
- @Override
- public void scheduleRemove(int sensorId, @NonNull IBinder token,
- @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
- @NonNull String opPackageName) {
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
-
- if (Flags.deHidl()) {
- scheduleRemoveAidl(token, receiver, fingerId, userId, opPackageName);
- } else {
- scheduleRemoveHidl(token, receiver, fingerId, userId, opPackageName);
- }
- });
- }
-
- private void scheduleRemoveHidl(@NonNull IBinder token,
- @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
- @NonNull String opPackageName) {
- final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
- userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
- }
-
- private void scheduleRemoveAidl(@NonNull IBinder token,
- @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
- @NonNull String opPackageName) {
- final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintRemovalClient client =
- new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintRemovalClient(
- mContext, this::getSession, token,
- new ClientMonitorCallbackConverter(receiver), new int[]{fingerId}, userId,
- opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
- }
-
- @Override
- public void scheduleRemoveAll(int sensorId, @NonNull IBinder token,
- @NonNull IFingerprintServiceReceiver receiver, int userId,
- @NonNull String opPackageName) {
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
-
- // For IBiometricsFingerprint@2.1, remove(0) means remove all enrollments
- if (Flags.deHidl()) {
- scheduleRemoveAidl(token, receiver, 0 /* fingerId */, userId, opPackageName);
- } else {
- scheduleRemoveHidl(token, receiver, 0 /* fingerId */, userId, opPackageName);
- }
- });
- }
-
- private void scheduleInternalCleanup(int userId,
- @Nullable ClientMonitorCallback callback) {
- mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
-
- if (Flags.deHidl()) {
- scheduleInternalCleanupAidl(userId, callback);
- } else {
- scheduleInternalCleanupHidl(userId, callback);
- }
- });
- }
-
- private void scheduleInternalCleanupHidl(int userId,
- @Nullable ClientMonitorCallback callback) {
- final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
- mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext,
- FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, callback);
- }
-
- private void scheduleInternalCleanupAidl(int userId,
- @Nullable ClientMonitorCallback callback) {
- final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintInternalCleanupClient
- client =
- new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintInternalCleanupClient(
- mContext, this::getSession, userId, mContext.getOpPackageName(),
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext,
- FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, callback);
- }
-
- @Override
- public void scheduleInternalCleanup(int sensorId, int userId,
- @Nullable ClientMonitorCallback callback) {
- scheduleInternalCleanup(userId, new ClientMonitorCompositeCallback(callback,
- mBiometricStateCallback));
- }
-
- @Override
- public void scheduleInternalCleanup(int sensorId, int userId,
- @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments) {
- scheduleInternalCleanup(userId, new ClientMonitorCompositeCallback(callback,
- mBiometricStateCallback));
- }
-
- private BiometricLogger createLogger(int statsAction, int statsClient,
- AuthenticationStatsCollector authenticationStatsCollector) {
- return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FINGERPRINT,
- statsAction, statsClient, authenticationStatsCollector);
- }
-
- @Override
- public boolean isHardwareDetected(int sensorId) {
- return getDaemon() != null;
- }
-
- @Override
- public void rename(int sensorId, int fingerId, int userId, @NonNull String name) {
- mHandler.post(() -> {
- FingerprintUtils.getLegacyInstance(mSensorId)
- .renameBiometricForUser(mContext, userId, fingerId, name);
- });
- }
-
- @Override
- @NonNull
- public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) {
- return FingerprintUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId);
- }
-
- @Override
- public boolean hasEnrollments(int sensorId, int userId) {
- return !getEnrolledFingerprints(sensorId, userId).isEmpty();
- }
-
- @Override
- @LockoutTracker.LockoutMode public int getLockoutModeForUser(int sensorId, int userId) {
- return mLockoutTracker.getLockoutModeForUser(userId);
- }
-
- @Override
- public long getAuthenticatorId(int sensorId, int userId) {
- return mAuthenticatorIds.getOrDefault(userId, 0L);
- }
-
- @Override
- public void onPointerDown(long requestId, int sensorId, PointerContext pc) {
- mScheduler.getCurrentClientIfMatches(requestId, (client) -> {
- if (!(client instanceof Udfps)) {
- Slog.w(TAG, "onFingerDown received during client: " + client);
- return;
- }
- ((Udfps) client).onPointerDown(pc);
- });
- }
-
- @Override
- public void onPointerUp(long requestId, int sensorId, PointerContext pc) {
- mScheduler.getCurrentClientIfMatches(requestId, (client) -> {
- if (!(client instanceof Udfps)) {
- Slog.w(TAG, "onFingerDown received during client: " + client);
- return;
- }
- ((Udfps) client).onPointerUp(pc);
- });
- }
-
- @Override
- public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event, long requestId,
- int sensorId) {
- mScheduler.getCurrentClientIfMatches(requestId, (client) -> {
- if (!(client instanceof Udfps)) {
- Slog.w(TAG, "onUdfpsUiEvent received during client: " + client);
- return;
- }
- ((Udfps) client).onUdfpsUiEvent(event);
- });
- }
-
- @Override
- public void onPowerPressed() {
- Slog.e(TAG, "onPowerPressed not supported for HIDL clients");
- }
-
- @Override
- public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
- mUdfpsOverlayController = controller;
- }
-
- // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
- @Override
- public void setSidefpsController(@NonNull ISidefpsController controller) {
- mSidefpsController = controller;
- }
-
- @Override
- public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
- boolean clearSchedulerBuffer) {
- final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
-
- proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
- proto.write(SensorStateProto.MODALITY, SensorStateProto.FINGERPRINT);
- if (mSensorProperties.isAnyUdfpsType()) {
- proto.write(SensorStateProto.MODALITY_FLAGS, SensorStateProto.FINGERPRINT_UDFPS);
- }
- proto.write(SensorStateProto.CURRENT_STRENGTH,
- Utils.getCurrentStrength(mSensorProperties.sensorId));
- proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
-
- for (UserInfo user : UserManager.get(mContext).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
-
- final long userToken = proto.start(SensorStateProto.USER_STATES);
- proto.write(UserStateProto.USER_ID, userId);
- proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getLegacyInstance(mSensorId)
- .getBiometricsForUser(mContext, userId).size());
- proto.end(userToken);
- }
-
- proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN,
- mSensorProperties.resetLockoutRequiresHardwareAuthToken);
- proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE,
- mSensorProperties.resetLockoutRequiresChallenge);
-
- proto.end(sensorToken);
- }
-
- @Override
- public void dumpProtoMetrics(int sensorId, FileDescriptor fd) {
- PerformanceTracker tracker =
- PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
-
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
- for (UserInfo user : UserManager.get(mContext).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
-
- final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
-
- proto.write(FingerprintUserStatsProto.USER_ID, userId);
- proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
- FingerprintUtils.getLegacyInstance(mSensorId)
- .getBiometricsForUser(mContext, userId).size());
-
- // Normal fingerprint authentications (e.g. lockscreen)
- long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
- proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptForUser(userId));
- proto.write(PerformanceStatsProto.REJECT, tracker.getRejectForUser(userId));
- proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireForUser(userId));
- proto.write(PerformanceStatsProto.LOCKOUT, tracker.getTimedLockoutForUser(userId));
- proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT,
- tracker.getPermanentLockoutForUser(userId));
- proto.end(countsToken);
-
- // Statistics about secure fingerprint transactions (e.g. to unlock password
- // storage, make secure purchases, etc.)
- countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
- proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptCryptoForUser(userId));
- proto.write(PerformanceStatsProto.REJECT, tracker.getRejectCryptoForUser(userId));
- proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireCryptoForUser(userId));
- proto.write(PerformanceStatsProto.LOCKOUT, 0); // meaningless for crypto
- proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, 0); // meaningless for crypto
- proto.end(countsToken);
-
- proto.end(userToken);
- }
- proto.flush();
- tracker.clear();
- }
-
- @Override
- public void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
- @NonNull IInvalidationCallback callback) {
- // TODO (b/179101888): Remove this temporary workaround.
- try {
- callback.onCompleted();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to complete InvalidateAuthenticatorId");
- }
- }
-
- @Override
- public void dumpInternal(int sensorId, @NonNull PrintWriter pw) {
- PerformanceTracker performanceTracker =
- PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
-
- JSONObject dump = new JSONObject();
- try {
- dump.put("service", TAG);
- dump.put("isUdfps", mIsUdfps);
- dump.put("isPowerbuttonFps", mIsPowerbuttonFps);
-
- JSONArray sets = new JSONArray();
- for (UserInfo user : UserManager.get(mContext).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
- final int N = FingerprintUtils.getLegacyInstance(mSensorId)
- .getBiometricsForUser(mContext, userId).size();
- JSONObject set = new JSONObject();
- set.put("id", userId);
- set.put("count", N);
- set.put("accept", performanceTracker.getAcceptForUser(userId));
- set.put("reject", performanceTracker.getRejectForUser(userId));
- set.put("acquire", performanceTracker.getAcquireForUser(userId));
- set.put("lockout", performanceTracker.getTimedLockoutForUser(userId));
- set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId));
- // cryptoStats measures statistics about secure fingerprint transactions
- // (e.g. to unlock password storage, make secure purchases, etc.)
- set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId));
- set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId));
- set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId));
- sets.put(set);
- }
-
- dump.put("prints", sets);
- } catch (JSONException e) {
- Slog.e(TAG, "dump formatting failure", e);
- }
- pw.println(dump);
- pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
- mScheduler.dump(pw);
- }
-
- void setTestHalEnabled(boolean enabled) {
- mTestHalEnabled = enabled;
- }
-
- @NonNull
- @Override
- public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
- @NonNull String opPackageName) {
- return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, callback,
- mBiometricStateCallback, this, mHalResultController);
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
deleted file mode 100644
index f857946..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ /dev/null
@@ -1,573 +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.biometrics.sensors.fingerprint.hidl;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.trust.TrustManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.PointerContext;
-import android.hardware.fingerprint.FingerprintAuthenticateOptions;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
-import android.hardware.fingerprint.FingerprintSensorProperties;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Slog;
-import android.util.SparseBooleanArray;
-
-import com.android.internal.R;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.sensors.AuthenticationConsumer;
-import com.android.server.biometrics.sensors.AuthenticationStateListeners;
-import com.android.server.biometrics.sensors.BaseClientMonitor;
-import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.BiometricStateCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.LockoutResetDispatcher;
-import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
-/**
- * A mockable/testable provider of the {@link android.hardware.biometrics.fingerprint.V2_3} HIDL
- * interface. This class is intended simulate UDFPS logic for devices that do not have an actual
- * fingerprint@2.3 HAL (where UDFPS starts to become supported)
- *
- * UDFPS "accept" can only happen within a set amount of time after a sensor authentication. This is
- * specified by {@link MockHalResultController#AUTH_VALIDITY_MS}. Touches after this duration will
- * be treated as "reject".
- *
- * This class provides framework logic to emulate, for testing only, the UDFPS functionalies below:
- *
- * 1) IF either A) the caller is keyguard, and the device is not in a trusted state (authenticated
- * via biometric sensor or unlocked with a trust agent {@see android.app.trust.TrustManager}, OR
- * B) the caller is not keyguard, and regardless of trusted state, AND (following applies to both
- * (A) and (B) above) {@link FingerprintManager#onFingerDown(int, int, float, float)} is
- * received, this class will respond with {@link AuthenticationCallback#onAuthenticationFailed()}
- * after a tunable flat_time + variance_time.
- *
- * In the case above (1), callers must not receive a successful authentication event here because
- * the sensor has not actually been authenticated.
- *
- * 2) IF A) the caller is keyguard and the device is not in a trusted state, OR B) the caller is not
- * keyguard and regardless of trusted state, AND (following applies to both (A) and (B)) the
- * sensor is touched and the fingerprint is accepted by the HAL, and then
- * {@link FingerprintManager#onFingerDown(int, int, float, float)} is received, this class will
- * respond with {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)}
- * after a tunable flat_time + variance_time. Note that the authentication callback from the
- * sensor is held until {@link FingerprintManager#onFingerDown(int, int, float, float)} is
- * invoked.
- *
- * In the case above (2), callers can receive a successful authentication callback because the
- * real sensor was authenticated. Note that even though the real sensor was touched, keyguard
- * fingerprint authentication does not put keyguard into a trusted state because the
- * authentication callback is held until onFingerDown was invoked. This allows callers such as
- * keyguard to simulate a realistic path.
- *
- * 3) IF the caller is keyguard AND the device in a trusted state and then
- * {@link FingerprintManager#onFingerDown(int, int, float, float)} is received, this class will
- * respond with {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)}
- * after a tunable flat_time + variance time.
- *
- * In the case above (3), since the device is already unlockable via trust agent, it's fine to
- * simulate the successful auth path. Non-keyguard clients will fall into either (1) or (2)
- * above.
- *
- * This class currently does not simulate false rejection. Conversely, this class relies on the
- * real hardware sensor so does not affect false acceptance.
- */
-@SuppressWarnings("deprecation")
-public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManager.TrustListener {
-
- private static final String TAG = "Fingerprint21UdfpsMock";
-
- // Secure setting integer. If true, the system will load this class to enable udfps testing.
- public static final String CONFIG_ENABLE_TEST_UDFPS =
- "com.android.server.biometrics.sensors.fingerprint.test_udfps.enable";
- // Secure setting integer. A fixed duration intended to simulate something like the duration
- // required for image capture.
- private static final String CONFIG_AUTH_DELAY_PT1 =
- "com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_pt1";
- // Secure setting integer. A fixed duration intended to simulate something like the duration
- // required for template matching to complete.
- private static final String CONFIG_AUTH_DELAY_PT2 =
- "com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_pt2";
- // Secure setting integer. A random value between [-randomness, randomness] will be added to the
- // capture delay above for each accept/reject.
- private static final String CONFIG_AUTH_DELAY_RANDOMNESS =
- "com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_randomness";
-
- private static final int DEFAULT_AUTH_DELAY_PT1_MS = 300;
- private static final int DEFAULT_AUTH_DELAY_PT2_MS = 400;
- private static final int DEFAULT_AUTH_DELAY_RANDOMNESS_MS = 100;
-
- @NonNull private final TestableBiometricScheduler mScheduler;
- @NonNull private final Handler mHandler;
- @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
- @NonNull private final MockHalResultController mMockHalResultController;
- @NonNull private final TrustManager mTrustManager;
- @NonNull private final SparseBooleanArray mUserHasTrust;
- @NonNull private final Random mRandom;
- @NonNull private final FakeRejectRunnable mFakeRejectRunnable;
- @NonNull private final FakeAcceptRunnable mFakeAcceptRunnable;
- @NonNull private final RestartAuthRunnable mRestartAuthRunnable;
-
- private static class TestableBiometricScheduler extends BiometricScheduler {
- @NonNull private Fingerprint21UdfpsMock mFingerprint21;
-
- TestableBiometricScheduler(
- @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- super(BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher);
- }
-
- void init(@NonNull Fingerprint21UdfpsMock fingerprint21) {
- mFingerprint21 = fingerprint21;
- }
- }
-
- /**
- * All of the mocking/testing should happen in here. This way we don't need to modify the
- * {@link BaseClientMonitor} implementations and can run the
- * real path there.
- */
- private static class MockHalResultController extends HalResultController {
-
- // Duration for which a sensor authentication can be treated as UDFPS success.
- private static final int AUTH_VALIDITY_MS = 10 * 1000; // 10 seconds
-
- static class LastAuthArgs {
- @NonNull final AuthenticationConsumer lastAuthenticatedClient;
- final long deviceId;
- final int fingerId;
- final int groupId;
- @Nullable final ArrayList<Byte> token;
-
- LastAuthArgs(@NonNull AuthenticationConsumer authenticationConsumer, long deviceId,
- int fingerId, int groupId, @Nullable ArrayList<Byte> token) {
- lastAuthenticatedClient = authenticationConsumer;
- this.deviceId = deviceId;
- this.fingerId = fingerId;
- this.groupId = groupId;
- if (token == null) {
- this.token = null;
- } else {
- // Store a copy so the owner can be GC'd
- this.token = new ArrayList<>(token);
- }
- }
- }
-
- // Initialized after the constructor, but before it's ever used.
- @NonNull private RestartAuthRunnable mRestartAuthRunnable;
- @NonNull private Fingerprint21UdfpsMock mFingerprint21;
- @Nullable private LastAuthArgs mLastAuthArgs;
-
- MockHalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
- @NonNull BiometricScheduler scheduler) {
- super(sensorId, context, handler, scheduler);
- }
-
- void init(@NonNull RestartAuthRunnable restartAuthRunnable,
- @NonNull Fingerprint21UdfpsMock fingerprint21) {
- mRestartAuthRunnable = restartAuthRunnable;
- mFingerprint21 = fingerprint21;
- }
-
- @Nullable AuthenticationConsumer getLastAuthenticatedClient() {
- return mLastAuthArgs != null ? mLastAuthArgs.lastAuthenticatedClient : null;
- }
-
- /**
- * Intercepts the HAL authentication and holds it until the UDFPS simulation decides
- * that authentication finished.
- */
- @Override
- public void onAuthenticated(long deviceId, int fingerId, int groupId,
- ArrayList<Byte> token) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(TAG, "Non authentication consumer: " + client);
- return;
- }
-
- final boolean accepted = fingerId != 0;
- if (accepted) {
- mFingerprint21.setDebugMessage("Finger accepted");
- } else {
- mFingerprint21.setDebugMessage("Finger rejected");
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- mLastAuthArgs = new LastAuthArgs(authenticationConsumer, deviceId, fingerId,
- groupId, token);
-
- // Remove any existing restart runnbables since auth just started, and put a new
- // one on the queue.
- mHandler.removeCallbacks(mRestartAuthRunnable);
- mRestartAuthRunnable.setLastAuthReference(authenticationConsumer);
- mHandler.postDelayed(mRestartAuthRunnable, AUTH_VALIDITY_MS);
- });
- }
-
- /**
- * Calls through to the real interface and notifies clients of accept/reject.
- */
- void sendAuthenticated(long deviceId, int fingerId, int groupId,
- ArrayList<Byte> token) {
- Slog.d(TAG, "sendAuthenticated: " + (fingerId != 0));
- mFingerprint21.setDebugMessage("Udfps match: " + (fingerId != 0));
- super.onAuthenticated(deviceId, fingerId, groupId, token);
- }
- }
-
- public static Fingerprint21UdfpsMock newInstance(@NonNull Context context,
- @NonNull BiometricStateCallback biometricStateCallback,
- @NonNull AuthenticationStateListeners authenticationStateListeners,
- @NonNull FingerprintSensorPropertiesInternal sensorProps,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull BiometricContext biometricContext) {
- Slog.d(TAG, "Creating Fingerprint23Mock!");
-
- final Handler handler = new Handler(Looper.getMainLooper());
- final TestableBiometricScheduler scheduler =
- new TestableBiometricScheduler(gestureAvailabilityDispatcher);
- final MockHalResultController controller =
- new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
- return new Fingerprint21UdfpsMock(context, biometricStateCallback,
- authenticationStateListeners, sensorProps, scheduler, handler,
- lockoutResetDispatcher, controller, biometricContext);
- }
-
- private static abstract class FakeFingerRunnable implements Runnable {
- private long mFingerDownTime;
- private int mCaptureDuration;
-
- /**
- * @param fingerDownTime System time when onFingerDown occurred
- * @param captureDuration Duration that the finger needs to be down for
- */
- void setSimulationTime(long fingerDownTime, int captureDuration) {
- mFingerDownTime = fingerDownTime;
- mCaptureDuration = captureDuration;
- }
-
- @SuppressWarnings("BooleanMethodIsAlwaysInverted")
- boolean isImageCaptureComplete() {
- return System.currentTimeMillis() - mFingerDownTime > mCaptureDuration;
- }
- }
-
- private final class FakeRejectRunnable extends FakeFingerRunnable {
- @Override
- public void run() {
- mMockHalResultController.sendAuthenticated(0, 0, 0, null);
- }
- }
-
- private final class FakeAcceptRunnable extends FakeFingerRunnable {
- @Override
- public void run() {
- if (mMockHalResultController.mLastAuthArgs == null) {
- // This can happen if the user has trust agents enabled, which make lockscreen
- // dismissable. Send a fake non-zero (accept) finger.
- Slog.d(TAG, "Sending fake finger");
- mMockHalResultController.sendAuthenticated(1 /* deviceId */,
- 1 /* fingerId */, 1 /* groupId */, null /* token */);
- } else {
- mMockHalResultController.sendAuthenticated(
- mMockHalResultController.mLastAuthArgs.deviceId,
- mMockHalResultController.mLastAuthArgs.fingerId,
- mMockHalResultController.mLastAuthArgs.groupId,
- mMockHalResultController.mLastAuthArgs.token);
- }
- }
- }
-
- /**
- * The fingerprint HAL allows multiple (5) fingerprint attempts per HIDL invocation of the
- * authenticate method. However, valid fingerprint authentications are invalidated after
- * {@link MockHalResultController#AUTH_VALIDITY_MS}, meaning UDFPS touches will be reported as
- * rejects if touched after that duration. However, since a valid fingerprint was detected, the
- * HAL and FingerprintService will not look for subsequent fingerprints.
- *
- * In order to keep the FingerprintManager API consistent (that multiple fingerprint attempts
- * are allowed per auth lifecycle), we internally cancel and restart authentication so that the
- * sensor is responsive again.
- */
- private static final class RestartAuthRunnable implements Runnable {
- @NonNull private final Fingerprint21UdfpsMock mFingerprint21;
- @NonNull private final TestableBiometricScheduler mScheduler;
-
- // Store a reference to the auth consumer that should be invalidated.
- private AuthenticationConsumer mLastAuthConsumer;
-
- RestartAuthRunnable(@NonNull Fingerprint21UdfpsMock fingerprint21,
- @NonNull TestableBiometricScheduler scheduler) {
- mFingerprint21 = fingerprint21;
- mScheduler = scheduler;
- }
-
- void setLastAuthReference(AuthenticationConsumer lastAuthConsumer) {
- mLastAuthConsumer = lastAuthConsumer;
- }
-
- @Override
- public void run() {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
-
- // We don't care about FingerprintDetectClient, since accept/rejects are both OK. UDFPS
- // rejects will just simulate the path where non-enrolled fingers are presented.
- if (!(client instanceof FingerprintAuthenticationClient)) {
- Slog.e(TAG, "Non-FingerprintAuthenticationClient client: " + client);
- return;
- }
-
- // Perhaps the runnable is stale, or the user stopped/started auth manually. Do not
- // restart auth in this case.
- if (client != mLastAuthConsumer) {
- Slog.e(TAG, "Current client: " + client
- + " does not match mLastAuthConsumer: " + mLastAuthConsumer);
- return;
- }
-
- Slog.d(TAG, "Restarting auth, current: " + client);
- mFingerprint21.setDebugMessage("Auth timed out");
-
- final FingerprintAuthenticationClient authClient =
- (FingerprintAuthenticationClient) client;
- // Store the authClient parameters so it can be rescheduled
- final IBinder token = client.getToken();
- final long operationId = authClient.getOperationId();
- final int cookie = client.getCookie();
- final ClientMonitorCallbackConverter listener = new ClientMonitorCallbackConverter(
- new IFingerprintServiceReceiver.Default());
- final boolean restricted = authClient.isRestricted();
- final int statsClient = client.getLogger().getStatsClient();
- final boolean isKeyguard = authClient.isKeyguard();
- final FingerprintAuthenticateOptions options =
- new FingerprintAuthenticateOptions.Builder()
- .setUserId(client.getTargetUserId())
- .setOpPackageName(client.getOwnerString())
- .build();
-
- // Don't actually send cancel() to the HAL, since successful auth already finishes
- // HAL authenticate() lifecycle. Just
- mScheduler.getInternalCallback().onClientFinished(client, true /* success */);
-
- // Schedule this only after we invoke onClientFinished for the previous client, so that
- // internal preemption logic is not run.
- mFingerprint21.scheduleAuthenticate(token,
- operationId, cookie, listener, options, restricted, statsClient,
- isKeyguard);
- }
- }
-
- private Fingerprint21UdfpsMock(@NonNull Context context,
- @NonNull BiometricStateCallback biometricStateCallback,
- @NonNull AuthenticationStateListeners authenticationStateListeners,
- @NonNull FingerprintSensorPropertiesInternal sensorProps,
- @NonNull TestableBiometricScheduler scheduler,
- @NonNull Handler handler,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull MockHalResultController controller,
- @NonNull BiometricContext biometricContext) {
- super(context, biometricStateCallback, authenticationStateListeners, sensorProps, scheduler,
- handler, lockoutResetDispatcher, controller, biometricContext);
- mScheduler = scheduler;
- mScheduler.init(this);
- mHandler = handler;
- // resetLockout is controlled by the framework, so hardwareAuthToken is not required
- final boolean resetLockoutRequiresHardwareAuthToken = false;
- final int maxTemplatesAllowed = mContext.getResources()
- .getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
- mSensorProperties = new FingerprintSensorPropertiesInternal(sensorProps.sensorId,
- sensorProps.sensorStrength, maxTemplatesAllowed, sensorProps.componentInfo,
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, false /* halControlsIllumination */,
- resetLockoutRequiresHardwareAuthToken, sensorProps.getAllLocations());
- mMockHalResultController = controller;
- mUserHasTrust = new SparseBooleanArray();
- mTrustManager = context.getSystemService(TrustManager.class);
- mTrustManager.registerTrustListener(this);
- mRandom = new Random();
- mFakeRejectRunnable = new FakeRejectRunnable();
- mFakeAcceptRunnable = new FakeAcceptRunnable();
- mRestartAuthRunnable = new RestartAuthRunnable(this, mScheduler);
-
- // We can't initialize this during MockHalresultController's constructor due to a circular
- // dependency.
- mMockHalResultController.init(mRestartAuthRunnable, this);
- }
-
- @Override
- public void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, int flags,
- List<String> trustGrantedMessages) {
- mUserHasTrust.put(userId, enabled);
- }
-
- @Override
- public void onTrustManagedChanged(boolean enabled, int userId) {
-
- }
-
- @Override
- public void onTrustError(CharSequence message) {
-
- }
-
- @Override
- public void onEnabledTrustAgentsChanged(int userId) {
-
- }
-
- @Override
- public void onIsActiveUnlockRunningChanged(boolean isRunning, int userId) {
-
- }
-
- @Override
- @NonNull
- public List<FingerprintSensorPropertiesInternal> getSensorProperties() {
- final List<FingerprintSensorPropertiesInternal> properties = new ArrayList<>();
- properties.add(mSensorProperties);
- return properties;
- }
-
- @Override
- public void onPointerDown(long requestId, int sensorId, PointerContext pc) {
- mHandler.post(() -> {
- Slog.d(TAG, "onFingerDown");
- final AuthenticationConsumer lastAuthenticatedConsumer =
- mMockHalResultController.getLastAuthenticatedClient();
- final BaseClientMonitor currentScheduledClient = mScheduler.getCurrentClient();
-
- if (currentScheduledClient == null) {
- Slog.d(TAG, "Not authenticating");
- return;
- }
-
- mHandler.removeCallbacks(mFakeRejectRunnable);
- mHandler.removeCallbacks(mFakeAcceptRunnable);
-
- // The sensor was authenticated, is still the currently scheduled client, and the
- // user touched the UDFPS affordance. Pretend that auth succeeded.
- final boolean authenticatedClientIsCurrent = lastAuthenticatedConsumer != null
- && lastAuthenticatedConsumer == currentScheduledClient;
- // User is unlocked on keyguard via Trust Agent
- final boolean keyguardAndTrusted;
- if (currentScheduledClient instanceof FingerprintAuthenticationClient) {
- keyguardAndTrusted = ((FingerprintAuthenticationClient) currentScheduledClient)
- .isKeyguard()
- && mUserHasTrust.get(currentScheduledClient.getTargetUserId(), false);
- } else {
- keyguardAndTrusted = false;
- }
-
- final int captureDuration = getNewCaptureDuration();
- final int matchingDuration = getMatchingDuration();
- final int totalDuration = captureDuration + matchingDuration;
- setDebugMessage("Duration: " + totalDuration
- + " (" + captureDuration + " + " + matchingDuration + ")");
- if (authenticatedClientIsCurrent || keyguardAndTrusted) {
- mFakeAcceptRunnable.setSimulationTime(System.currentTimeMillis(), captureDuration);
- mHandler.postDelayed(mFakeAcceptRunnable, totalDuration);
- } else if (currentScheduledClient instanceof AuthenticationConsumer) {
- // Something is authenticating but authentication has not succeeded yet. Pretend
- // that auth rejected.
- mFakeRejectRunnable.setSimulationTime(System.currentTimeMillis(), captureDuration);
- mHandler.postDelayed(mFakeRejectRunnable, totalDuration);
- }
- });
- }
-
- @Override
- public void onPointerUp(long requestId, int sensorId, PointerContext pc) {
- mHandler.post(() -> {
- Slog.d(TAG, "onFingerUp");
-
- // Only one of these can be on the handler at any given time (see onFingerDown). If
- // image capture is not complete, send ACQUIRED_TOO_FAST and remove the runnable from
- // the handler. Image capture (onFingerDown) needs to happen again.
- if (mHandler.hasCallbacks(mFakeRejectRunnable)
- && !mFakeRejectRunnable.isImageCaptureComplete()) {
- mHandler.removeCallbacks(mFakeRejectRunnable);
- mMockHalResultController.onAcquired(0 /* deviceId */,
- FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST,
- 0 /* vendorCode */);
- } else if (mHandler.hasCallbacks(mFakeAcceptRunnable)
- && !mFakeAcceptRunnable.isImageCaptureComplete()) {
- mHandler.removeCallbacks(mFakeAcceptRunnable);
- mMockHalResultController.onAcquired(0 /* deviceId */,
- FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST,
- 0 /* vendorCode */);
- }
- });
- }
-
- private int getNewCaptureDuration() {
- final ContentResolver contentResolver = mContext.getContentResolver();
- final int captureTime = Settings.Secure.getIntForUser(contentResolver,
- CONFIG_AUTH_DELAY_PT1,
- DEFAULT_AUTH_DELAY_PT1_MS,
- UserHandle.USER_CURRENT);
- final int randomDelayRange = Settings.Secure.getIntForUser(contentResolver,
- CONFIG_AUTH_DELAY_RANDOMNESS,
- DEFAULT_AUTH_DELAY_RANDOMNESS_MS,
- UserHandle.USER_CURRENT);
- final int randomDelay = mRandom.nextInt(randomDelayRange * 2) - randomDelayRange;
-
- // Must be at least 0
- return Math.max(captureTime + randomDelay, 0);
- }
-
- private int getMatchingDuration() {
- final int matchingTime = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- CONFIG_AUTH_DELAY_PT2,
- DEFAULT_AUTH_DELAY_PT2_MS,
- UserHandle.USER_CURRENT);
-
- // Must be at least 0
- return Math.max(matchingTime, 0);
- }
-
- private void setDebugMessage(String message) {
- try {
- final IUdfpsOverlayController controller = getUdfpsOverlayController();
- // Things can happen before SysUI loads and sets the controller.
- if (controller != null) {
- Slog.d(TAG, "setDebugMessage: " + message);
- controller.setDebugMessage(mSensorProperties.sensorId, message);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending message: " + message, e);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
deleted file mode 100644
index b6311af..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ /dev/null
@@ -1,315 +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.biometrics.sensors.fingerprint.hidl;
-
-import static android.adaptiveauth.Flags.reportBiometricAuthAttempts;
-
-import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.TaskStackListener;
-import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.BiometricManager.Authenticators;
-import android.hardware.biometrics.BiometricSourceType;
-import android.hardware.biometrics.fingerprint.PointerContext;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintAuthenticateOptions;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.fingerprint.ISidefpsController;
-import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.log.CallbackWithProbe;
-import com.android.server.biometrics.log.Probe;
-import com.android.server.biometrics.sensors.AuthenticationClient;
-import com.android.server.biometrics.sensors.AuthenticationStateListeners;
-import com.android.server.biometrics.sensors.BiometricNotificationUtils;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
-import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.PerformanceTracker;
-import com.android.server.biometrics.sensors.SensorOverlays;
-import com.android.server.biometrics.sensors.fingerprint.Udfps;
-import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
-
-import java.util.ArrayList;
-import java.util.function.Supplier;
-
-/**
- * Fingerprint-specific authentication client supporting the
- * {@link android.hardware.biometrics.fingerprint.V2_1} and
- * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
- */
-class FingerprintAuthenticationClient
- extends AuthenticationClient<IBiometricsFingerprint, FingerprintAuthenticateOptions>
- implements Udfps {
-
- private static final String TAG = "Biometrics/FingerprintAuthClient";
-
- private final LockoutFrameworkImpl mLockoutFrameworkImpl;
- @NonNull private final SensorOverlays mSensorOverlays;
- @NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
- @NonNull private final CallbackWithProbe<Probe> mALSProbeCallback;
- @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
-
- private boolean mIsPointerDown;
-
- FingerprintAuthenticationClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFingerprint> lazyDaemon,
- @NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener, long operationId,
- boolean restricted, @NonNull FingerprintAuthenticateOptions options,
- int cookie, boolean requireConfirmation, @NonNull BiometricLogger logger,
- @NonNull BiometricContext biometricContext, boolean isStrongBiometric,
- @NonNull TaskStackListener taskStackListener,
- @NonNull LockoutFrameworkImpl lockoutTracker,
- @Nullable IUdfpsOverlayController udfpsOverlayController,
- // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
- @Nullable ISidefpsController sidefpsController,
- @NonNull AuthenticationStateListeners authenticationStateListeners,
- boolean allowBackgroundAuthentication,
- @NonNull FingerprintSensorPropertiesInternal sensorProps,
- @Authenticators.Types int sensorStrength) {
- super(context, lazyDaemon, token, listener, operationId, restricted,
- options, cookie, requireConfirmation, logger, biometricContext,
- isStrongBiometric, taskStackListener, lockoutTracker, allowBackgroundAuthentication,
- false /* shouldVibrate */, sensorStrength);
- setRequestId(requestId);
- mLockoutFrameworkImpl = lockoutTracker;
- if (sidefpsControllerRefactor()) {
- mSensorOverlays = new SensorOverlays(udfpsOverlayController);
- } else {
- mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
- }
- mAuthenticationStateListeners = authenticationStateListeners;
- mSensorProps = sensorProps;
- mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
- }
-
- @Override
- public void start(@NonNull ClientMonitorCallback callback) {
- super.start(callback);
-
- if (mSensorProps.isAnyUdfpsType()) {
- // UDFPS requires user to touch before becoming "active"
- mState = STATE_STARTED_PAUSED;
- } else {
- mState = STATE_STARTED;
- }
- }
-
- @NonNull
- @Override
- protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
- return new ClientMonitorCompositeCallback(mALSProbeCallback, callback);
- }
-
- @Override
- public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated, ArrayList<Byte> token) {
- super.onAuthenticated(identifier, authenticated, token);
-
- // Authentication lifecycle ends either when
- // 1) Authenticated == true
- // 2) Error occurred (lockout or some other error)
- // Note that authentication doesn't end when Authenticated == false
-
- if (authenticated) {
- mState = STATE_STOPPED;
- resetFailedAttempts(getTargetUserId());
- mSensorOverlays.hide(getSensorId());
- if (sidefpsControllerRefactor()) {
- mAuthenticationStateListeners.onAuthenticationStopped();
- }
- if (reportBiometricAuthAttempts()) {
- mAuthenticationStateListeners.onAuthenticationSucceeded(getRequestReason(),
- getTargetUserId());
- }
- } else {
- mState = STATE_STARTED_PAUSED_ATTEMPTED;
- final @LockoutTracker.LockoutMode int lockoutMode =
- mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId());
- if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
- Slog.w(TAG, "Fingerprint locked out, lockoutMode(" + lockoutMode + ")");
- final int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
- ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
- : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
- // Send the error, but do not invoke the FinishCallback yet. Since lockout is not
- // controlled by the HAL, the framework must stop the sensor before finishing the
- // client.
- mSensorOverlays.hide(getSensorId());
- if (sidefpsControllerRefactor()) {
- mAuthenticationStateListeners.onAuthenticationStopped();
- }
- onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
- cancel();
- }
- if (reportBiometricAuthAttempts()) {
- mAuthenticationStateListeners.onAuthenticationFailed(getRequestReason(),
- getTargetUserId());
- }
- }
- }
-
- @Override
- public void onError(int errorCode, int vendorCode) {
- super.onError(errorCode, vendorCode);
-
- if (errorCode == BiometricFingerprintConstants.FINGERPRINT_ERROR_BAD_CALIBRATION) {
- BiometricNotificationUtils.showBadCalibrationNotification(getContext());
- }
-
- mSensorOverlays.hide(getSensorId());
- if (sidefpsControllerRefactor()) {
- mAuthenticationStateListeners.onAuthenticationStopped();
- }
- }
-
- private void resetFailedAttempts(int userId) {
- mLockoutFrameworkImpl.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId);
- }
-
- @Override
- protected void handleLifecycleAfterAuth(boolean authenticated) {
- if (authenticated) {
- mCallback.onClientFinished(this, true /* success */);
- }
- }
-
- @Override
- public void onAcquired(int acquiredInfo, int vendorCode) {
- mAuthenticationStateListeners.onAuthenticationAcquired(
- BiometricSourceType.FINGERPRINT, getRequestReason(), acquiredInfo);
- super.onAcquired(acquiredInfo, vendorCode);
-
- @LockoutTracker.LockoutMode final int lockoutMode =
- getLockoutTracker().getLockoutModeForUser(getTargetUserId());
- if (lockoutMode == LockoutTracker.LOCKOUT_NONE) {
- PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
- pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
- }
- }
-
- @Override
- public boolean wasUserDetected() {
- // TODO: Update if it needs to be used for fingerprint, i.e. success/reject, error_timeout
- return false;
- }
-
- @Override
- public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
- mLockoutFrameworkImpl.addFailedAttemptForUser(userId);
- @LockoutTracker.LockoutMode final int lockoutMode =
- getLockoutTracker().getLockoutModeForUser(userId);
- final PerformanceTracker performanceTracker =
- PerformanceTracker.getInstanceForSensorId(getSensorId());
- if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
- performanceTracker.incrementPermanentLockoutForUser(userId);
- } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
- performanceTracker.incrementTimedLockoutForUser(userId);
- }
-
- return lockoutMode;
- }
-
- @Override
- protected void startHalOperation() {
- mSensorOverlays.show(getSensorId(), getRequestReason(), this);
- if (sidefpsControllerRefactor()) {
- mAuthenticationStateListeners.onAuthenticationStarted(getRequestReason());
- }
-
- try {
- // GroupId was never used. In fact, groupId is always the same as userId.
- getFreshDaemon().authenticate(mOperationId, getTargetUserId());
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting auth", e);
- onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
- mSensorOverlays.hide(getSensorId());
- if (sidefpsControllerRefactor()) {
- mAuthenticationStateListeners.onAuthenticationStopped();
- }
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- @Override
- protected void stopHalOperation() {
- mSensorOverlays.hide(getSensorId());
- if (sidefpsControllerRefactor()) {
- mAuthenticationStateListeners.onAuthenticationStopped();
- }
-
- try {
- getFreshDaemon().cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- @Override
- public void onPointerDown(PointerContext pc) {
- mIsPointerDown = true;
- mState = STATE_STARTED;
- mALSProbeCallback.getProbe().enable();
- UdfpsHelper.onFingerDown(getFreshDaemon(), (int) pc.x, (int) pc.y, pc.minor, pc.major);
-
- try {
- getListener().onUdfpsPointerDown(getSensorId());
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
-
- @Override
- public void onPointerUp(PointerContext pc) {
- mIsPointerDown = false;
- mState = STATE_STARTED_PAUSED_ATTEMPTED;
- mALSProbeCallback.getProbe().disable();
- UdfpsHelper.onFingerUp(getFreshDaemon());
-
- try {
- getListener().onUdfpsPointerUp(getSensorId());
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
-
- @Override
- public boolean isPointerDown() {
- return mIsPointerDown;
- }
-
- @Override
- public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event) {
- // Unsupported in HIDL.
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
deleted file mode 100644
index 50e48fe..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ /dev/null
@@ -1,172 +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.biometrics.sensors.fingerprint.hidl;
-
-import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.BiometricRequestConstants;
-import android.hardware.biometrics.fingerprint.PointerContext;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintAuthenticateOptions;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.BiometricsProto;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.AcquisitionClient;
-import com.android.server.biometrics.sensors.AuthenticationConsumer;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.PerformanceTracker;
-import com.android.server.biometrics.sensors.SensorOverlays;
-import com.android.server.biometrics.sensors.fingerprint.Udfps;
-import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
-
-import java.util.ArrayList;
-import java.util.function.Supplier;
-
-/**
- * Performs fingerprint detection without exposing any matching information (e.g. accept/reject
- * have the same haptic, lockout counter is not increased).
- */
-class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
- implements AuthenticationConsumer, Udfps {
-
- private static final String TAG = "FingerprintDetectClient";
-
- private final boolean mIsStrongBiometric;
- @NonNull private final SensorOverlays mSensorOverlays;
- private boolean mIsPointerDown;
-
- public FingerprintDetectClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFingerprint> lazyDaemon,
- @NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener,
- @NonNull FingerprintAuthenticateOptions options,
- @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
- @Nullable IUdfpsOverlayController udfpsOverlayController,
- boolean isStrongBiometric) {
- super(context, lazyDaemon, token, listener, options.getUserId(),
- options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
- true /* shouldVibrate */, biometricLogger, biometricContext);
- setRequestId(requestId);
- if (sidefpsControllerRefactor()) {
- mSensorOverlays = new SensorOverlays(udfpsOverlayController);
- } else {
- mSensorOverlays = new SensorOverlays(
- udfpsOverlayController, null /* sideFpsController */);
- }
- mIsStrongBiometric = isStrongBiometric;
- }
-
- @Override
- protected void stopHalOperation() {
- mSensorOverlays.hide(getSensorId());
-
- try {
- getFreshDaemon().cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- @Override
- public void start(@NonNull ClientMonitorCallback callback) {
- super.start(callback);
- startHalOperation();
- }
-
- @Override
- protected void startHalOperation() {
- mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD,
- this);
-
- try {
- getFreshDaemon().authenticate(0 /* operationId */, getTargetUserId());
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting auth", e);
- onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
- mSensorOverlays.hide(getSensorId());
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- @Override
- public void onPointerDown(PointerContext pc) {
- mIsPointerDown = true;
- UdfpsHelper.onFingerDown(getFreshDaemon(), (int) pc.x, (int) pc.y, pc.minor, pc.major);
- }
-
- @Override
- public void onPointerUp(PointerContext pc) {
- mIsPointerDown = false;
- UdfpsHelper.onFingerUp(getFreshDaemon());
- }
-
- @Override
- public boolean isPointerDown() {
- return mIsPointerDown;
- }
-
- @Override
- public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event) {
- // Unsupported in HIDL.
- }
-
- @Override
- public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated, ArrayList<Byte> hardwareAuthToken) {
- getLogger().logOnAuthenticated(getContext(), getOperationContext(),
- authenticated, false /* requireConfirmation */,
- getTargetUserId(), false /* isBiometricPrompt */);
-
- // Do not distinguish between success/failures.
- vibrateSuccess();
-
- final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
- pm.incrementAuthForUser(getTargetUserId(), authenticated);
-
- try {
- getListener().onDetected(getSensorId(), getTargetUserId(), mIsStrongBiometric);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending onDetected", e);
- }
- }
-
- @Override
- public int getProtoEnum() {
- return BiometricsProto.CM_DETECT_INTERACTION;
- }
-
- @Override
- public boolean interruptsPrecedingClients() {
- return true;
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
deleted file mode 100644
index 8f937fc..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ /dev/null
@@ -1,229 +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.biometrics.sensors.fingerprint.hidl;
-
-import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.BiometricStateListener;
-import android.hardware.biometrics.fingerprint.PointerContext;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.FingerprintEnrollOptions;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.ISidefpsController;
-import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.AuthenticationStateListeners;
-import com.android.server.biometrics.sensors.BiometricNotificationUtils;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
-import com.android.server.biometrics.sensors.EnrollClient;
-import com.android.server.biometrics.sensors.SensorOverlays;
-import com.android.server.biometrics.sensors.fingerprint.Udfps;
-import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
-
-import java.util.function.Supplier;
-
-/**
- * Fingerprint-specific enroll client supporting the
- * {@link android.hardware.biometrics.fingerprint.V2_1} and
- * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
- */
-public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint>
- implements Udfps {
-
- private static final String TAG = "FingerprintEnrollClient";
-
- @NonNull private final SensorOverlays mSensorOverlays;
- private final @FingerprintManager.EnrollReason int mEnrollReason;
- @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
- private boolean mIsPointerDown;
-
- FingerprintEnrollClient(
- @NonNull Context context, @NonNull Supplier<IBiometricsFingerprint> lazyDaemon,
- @NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull byte[] hardwareAuthToken, @NonNull String owner,
- @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
- @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
- @Nullable IUdfpsOverlayController udfpsOverlayController,
- // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
- @Nullable ISidefpsController sidefpsController,
- @NonNull AuthenticationStateListeners authenticationStateListeners,
- @FingerprintManager.EnrollReason int enrollReason,
- @NonNull FingerprintEnrollOptions options) {
- super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- timeoutSec, sensorId, true /* shouldVibrate */, biometricLogger,
- biometricContext,
- BiometricFingerprintConstants.reasonToMetric(options.getEnrollReason()));
- setRequestId(requestId);
- if (sidefpsControllerRefactor()) {
- mSensorOverlays = new SensorOverlays(udfpsOverlayController);
- } else {
- mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
- }
- mAuthenticationStateListeners = authenticationStateListeners;
-
- mEnrollReason = enrollReason;
- if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
- getLogger().disableMetrics();
- }
- Slog.w(TAG, "EnrollOptions "
- + FingerprintEnrollOptions.enrollReasonToString(options.getEnrollReason()));
- }
-
- @Override
- public void start(@NonNull ClientMonitorCallback callback) {
- super.start(callback);
-
- BiometricNotificationUtils.cancelFingerprintEnrollNotification(getContext());
- }
-
- @NonNull
- @Override
- protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
- return new ClientMonitorCompositeCallback(
- getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
- }
-
- @Override
- protected boolean hasReachedEnrollmentLimit() {
- final int limit = getContext().getResources().getInteger(
- com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
- final int enrolled = mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId())
- .size();
- if (enrolled >= limit) {
- Slog.w(TAG, "Too many fingerprints registered, user: " + getTargetUserId());
- return true;
- }
- return false;
- }
-
- @Override
- protected void startHalOperation() {
- mSensorOverlays.show(getSensorId(), getRequestReasonFromEnrollReason(mEnrollReason),
- this);
- if (sidefpsControllerRefactor()) {
- mAuthenticationStateListeners.onAuthenticationStarted(
- getRequestReasonFromEnrollReason(mEnrollReason));
- }
-
- BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
- try {
- // GroupId was never used. In fact, groupId is always the same as userId.
- getFreshDaemon().enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting enroll", e);
- onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
- mSensorOverlays.hide(getSensorId());
- if (sidefpsControllerRefactor()) {
- mAuthenticationStateListeners.onAuthenticationStopped();
- }
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- @Override
- protected void stopHalOperation() {
- mSensorOverlays.hide(getSensorId());
- if (sidefpsControllerRefactor()) {
- mAuthenticationStateListeners.onAuthenticationStopped();
- }
-
- try {
- getFreshDaemon().cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- @Override
- public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
- super.onEnrollResult(identifier, remaining);
-
- mSensorOverlays.ifUdfps(
- controller -> controller.onEnrollmentProgress(getSensorId(), remaining));
-
- if (remaining == 0) {
- mSensorOverlays.hide(getSensorId());
- if (sidefpsControllerRefactor()) {
- mAuthenticationStateListeners.onAuthenticationStopped();
- }
- }
- }
-
- @Override
- public void onAcquired(int acquiredInfo, int vendorCode) {
- super.onAcquired(acquiredInfo, vendorCode);
-
- mSensorOverlays.ifUdfps(controller -> {
- if (UdfpsHelper.isValidAcquisitionMessage(getContext(), acquiredInfo, vendorCode)) {
- controller.onEnrollmentHelp(getSensorId());
- }
- });
-
- mCallback.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
- }
-
- @Override
- public void onError(int errorCode, int vendorCode) {
- super.onError(errorCode, vendorCode);
-
- mSensorOverlays.hide(getSensorId());
- if (sidefpsControllerRefactor()) {
- mAuthenticationStateListeners.onAuthenticationStopped();
- }
- }
-
- @Override
- public void onPointerDown(PointerContext pc) {
- mIsPointerDown = true;
- UdfpsHelper.onFingerDown(getFreshDaemon(), (int) pc.x, (int) pc.y, pc.minor, pc.major);
- }
-
- @Override
- public void onPointerUp(PointerContext pc) {
- mIsPointerDown = false;
- UdfpsHelper.onFingerUp(getFreshDaemon());
- }
-
- @Override
- public boolean isPointerDown() {
- return mIsPointerDown;
- }
-
- @Override
- public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event) {
- // Unsupported in HIDL.
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
deleted file mode 100644
index 3bb7135..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
+++ /dev/null
@@ -1,68 +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.biometrics.sensors.fingerprint.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.GenerateChallengeClient;
-
-import java.util.function.Supplier;
-
-/**
- * Fingerprint-specific generateChallenge/preEnroll client supporting the
- * {@link android.hardware.biometrics.fingerprint.V2_1} and
- * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
- */
-public class FingerprintGenerateChallengeClient
- extends GenerateChallengeClient<IBiometricsFingerprint> {
-
- private static final String TAG = "FingerprintGenerateChallengeClient";
-
- FingerprintGenerateChallengeClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId, @NonNull BiometricLogger logger,
- @NonNull BiometricContext biometricContext) {
- super(context, lazyDaemon, token, listener, userId, owner, sensorId, logger,
- biometricContext);
- }
-
- @Override
- protected void startHalOperation() {
- try {
- final long challenge = getFreshDaemon().preEnroll();
- try {
- getListener().onChallengeGenerated(getSensorId(), getTargetUserId(), challenge);
- mCallback.onClientFinished(this, true /* success */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- mCallback.onClientFinished(this, false /* success */);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "preEnroll failed", e);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
deleted file mode 100644
index 8b61f59..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
+++ /dev/null
@@ -1,76 +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.biometrics.sensors.fingerprint.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.Fingerprint;
-import android.os.IBinder;
-
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.InternalCleanupClient;
-import com.android.server.biometrics.sensors.InternalEnumerateClient;
-import com.android.server.biometrics.sensors.RemovalClient;
-
-import java.util.List;
-import java.util.Map;
-import java.util.function.Supplier;
-
-/**
- * Fingerprint-specific internal cleanup client supporting the
- * {@link android.hardware.biometrics.fingerprint.V2_1} and
- * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
- */
-class FingerprintInternalCleanupClient
- extends InternalCleanupClient<Fingerprint, IBiometricsFingerprint> {
-
- FingerprintInternalCleanupClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
- @NonNull String owner, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull BiometricUtils<Fingerprint> utils,
- @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
- utils, authenticatorIds);
- }
-
- @Override
- protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient(
- Context context, Supplier<IBiometricsFingerprint> lazyDaemon, IBinder token,
- int userId, String owner, List<Fingerprint> enrolledList,
- BiometricUtils<Fingerprint> utils, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
- return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
- enrolledList, utils, sensorId, logger, biometricContext);
- }
-
- @Override
- protected RemovalClient<Fingerprint, IBiometricsFingerprint> getRemovalClient(Context context,
- Supplier<IBiometricsFingerprint> lazyDaemon, IBinder token,
- int biometricId, int userId, String owner, BiometricUtils<Fingerprint> utils,
- int sensorId, @NonNull BiometricLogger logger,
- @NonNull BiometricContext biometricContext, Map<Integer, Long> authenticatorIds) {
- // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
- // is all done internally.
- return new FingerprintRemovalClient(context, lazyDaemon, token,
- null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
- sensorId, logger, biometricContext, authenticatorIds);
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
deleted file mode 100644
index 0840f1b..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
+++ /dev/null
@@ -1,61 +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.biometrics.sensors.fingerprint.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.Fingerprint;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.InternalEnumerateClient;
-
-import java.util.List;
-import java.util.function.Supplier;
-
-/**
- * Fingerprint-specific internal enumerate client supporting the
- * {@link android.hardware.biometrics.fingerprint.V2_1} and
- * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
- */
-class FingerprintInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFingerprint> {
- private static final String TAG = "FingerprintInternalEnumerateClient";
-
- FingerprintInternalEnumerateClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
- @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
- super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
- logger, biometricContext);
- }
-
- @Override
- protected void startHalOperation() {
- try {
- getFreshDaemon().enumerate();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting enumerate", e);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
deleted file mode 100644
index 9ec56c2..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
+++ /dev/null
@@ -1,67 +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.biometrics.sensors.fingerprint.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.Fingerprint;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.RemovalClient;
-
-import java.util.Map;
-import java.util.function.Supplier;
-
-/**
- * Fingerprint-specific removal client supporting the
- * {@link android.hardware.biometrics.fingerprint.V2_1} and
- * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
- */
-class FingerprintRemovalClient extends RemovalClient<Fingerprint, IBiometricsFingerprint> {
- private static final String TAG = "FingerprintRemovalClient";
-
- private final int mBiometricId;
-
- FingerprintRemovalClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
- @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
- logger, biometricContext, authenticatorIds);
- mBiometricId = biometricId;
- }
-
- @Override
- protected void startHalOperation() {
- try {
- // GroupId was never used. In fact, groupId is always the same as userId.
- getFreshDaemon().remove(getTargetUserId(), mBiometricId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting remove", e);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
deleted file mode 100644
index 843fcc8..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2021 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.biometrics.sensors.fingerprint.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-
-import com.android.server.biometrics.BiometricsProto;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.BaseClientMonitor;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-
-/**
- * Clears lockout, which is handled in the framework (and not the HAL) for the
- * IBiometricsFingerprint@2.1 interface.
- */
-public class FingerprintResetLockoutClient extends BaseClientMonitor {
-
- @NonNull final LockoutFrameworkImpl mLockoutTracker;
-
- public FingerprintResetLockoutClient(@NonNull Context context, int userId,
- @NonNull String owner, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull LockoutFrameworkImpl lockoutTracker) {
- super(context, null /* token */, null /* listener */, userId, owner, 0 /* cookie */,
- sensorId, logger, biometricContext);
- mLockoutTracker = lockoutTracker;
- }
-
- @Override
- public void start(@NonNull ClientMonitorCallback callback) {
- super.start(callback);
- mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
- getTargetUserId());
- callback.onClientFinished(this, true /* success */);
- }
-
- public boolean interruptsPrecedingClients() {
- return true;
- }
-
- @Override
- public int getProtoEnum() {
- return BiometricsProto.CM_RESET_LOCKOUT;
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
deleted file mode 100644
index 6273417..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
+++ /dev/null
@@ -1,59 +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.biometrics.sensors.fingerprint.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.RevokeChallengeClient;
-
-import java.util.function.Supplier;
-
-/**
- * Fingerprint-specific revokeChallenge client supporting the
- * {@link android.hardware.biometrics.fingerprint.V2_1} and
- * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
- */
-public class FingerprintRevokeChallengeClient
- extends RevokeChallengeClient<IBiometricsFingerprint> {
-
- private static final String TAG = "FingerprintRevokeChallengeClient";
-
- FingerprintRevokeChallengeClient(@NonNull Context context,
- @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- int userId, @NonNull String owner, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
- super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext);
- }
-
- @Override
- protected void startHalOperation() {
- try {
- getFreshDaemon().postEnroll();
- mCallback.onClientFinished(this, true /* success */);
- } catch (RemoteException e) {
- Slog.e(TAG, "revokeChallenge/postEnroll failed", e);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClientLegacy.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClientLegacy.java
deleted file mode 100644
index fc85402..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClientLegacy.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.biometrics.sensors.fingerprint.hidl;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.os.Build;
-import android.os.Environment;
-import android.os.RemoteException;
-import android.os.SELinux;
-import android.util.Slog;
-
-import com.android.server.biometrics.BiometricsProto;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.HalClientMonitor;
-
-import java.io.File;
-import java.util.Map;
-import java.util.function.Supplier;
-
-/**
- * TODO(b/304604965): Delete this class once Flags.DE_HIDL is ready for release.
- */
-public class FingerprintUpdateActiveUserClientLegacy extends
- HalClientMonitor<IBiometricsFingerprint> {
- private static final String TAG = "FingerprintUpdateActiveUserClient";
- private static final String FP_DATA_DIR = "fpdata";
-
- private final Supplier<Integer> mCurrentUserId;
- private final boolean mForceUpdateAuthenticatorId;
- private final boolean mHasEnrolledBiometrics;
- private final Map<Integer, Long> mAuthenticatorIds;
- private File mDirectory;
-
- FingerprintUpdateActiveUserClientLegacy(@NonNull Context context,
- @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
- @NonNull String owner, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull Supplier<Integer> currentUserId,
- boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds,
- boolean forceUpdateAuthenticatorId) {
- super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, logger, biometricContext);
- mCurrentUserId = currentUserId;
- mForceUpdateAuthenticatorId = forceUpdateAuthenticatorId;
- mHasEnrolledBiometrics = hasEnrolledBiometrics;
- mAuthenticatorIds = authenticatorIds;
- }
-
- @Override
- public void start(@NonNull ClientMonitorCallback callback) {
- super.start(callback);
-
- if (mCurrentUserId.get() == getTargetUserId() && !mForceUpdateAuthenticatorId) {
- Slog.d(TAG, "Already user: " + mCurrentUserId + ", returning");
- callback.onClientFinished(this, true /* success */);
- return;
- }
-
- int firstSdkInt = Build.VERSION.DEVICE_INITIAL_SDK_INT;
- if (firstSdkInt < Build.VERSION_CODES.BASE) {
- Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be "
- + "at least VERSION_CODES.BASE");
- }
- File baseDir;
- if (firstSdkInt <= Build.VERSION_CODES.O_MR1) {
- baseDir = Environment.getUserSystemDirectory(getTargetUserId());
- } else {
- baseDir = Environment.getDataVendorDeDirectory(getTargetUserId());
- }
-
- mDirectory = new File(baseDir, FP_DATA_DIR);
- if (!mDirectory.exists()) {
- if (!mDirectory.mkdir()) {
- Slog.e(TAG, "Cannot make directory: " + mDirectory.getAbsolutePath());
- callback.onClientFinished(this, false /* success */);
- return;
- }
- // Calling mkdir() from this process will create a directory with our
- // permissions (inherited from the containing dir). This command fixes
- // the label.
- if (!SELinux.restorecon(mDirectory)) {
- Slog.e(TAG, "Restorecons failed. Directory will have wrong label.");
- callback.onClientFinished(this, false /* success */);
- return;
- }
- }
-
- startHalOperation();
- }
-
- @Override
- public void unableToStart() {
- // Nothing to do here
- }
-
- @Override
- protected void startHalOperation() {
- try {
- final int targetId = getTargetUserId();
- Slog.d(TAG, "Setting active user: " + targetId);
- getFreshDaemon().setActiveGroup(targetId, mDirectory.getAbsolutePath());
- mAuthenticatorIds.put(targetId, mHasEnrolledBiometrics
- ? getFreshDaemon().getAuthenticatorId() : 0L);
- mCallback.onClientFinished(this, true /* success */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to setActiveGroup: " + e);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
-
- @Override
- public int getProtoEnum() {
- return BiometricsProto.CM_UPDATE_ACTIVE_USER;
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
index 47fdcdb..3214b6d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
@@ -186,7 +186,7 @@
mLockoutTracker,
mLockoutResetDispatcher,
mAuthSessionCoordinator,
- () -> {}, mAidlResponseHandlerCallback);
+ mAidlResponseHandlerCallback);
}
@VisibleForTesting IBiometricsFingerprint getIBiometricsFingerprint() {
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
index 16514fa..e6de14b 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
@@ -29,7 +29,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -122,7 +121,8 @@
+ " without permission " + Manifest.permission.DUMP);
return;
}
- IndentingPrintWriter radioPrintWriter = new IndentingPrintWriter(printWriter);
+ android.util.IndentingPrintWriter radioPrintWriter =
+ new android.util.IndentingPrintWriter(printWriter);
radioPrintWriter.printf("BroadcastRadioService\n");
radioPrintWriter.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
index ab08342..93fb7b2 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
@@ -26,7 +26,6 @@
import android.hardware.radio.RadioManager;
import android.os.Binder;
import android.os.RemoteException;
-import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
@@ -139,7 +138,7 @@
+ " without permission " + Manifest.permission.DUMP);
return;
}
- IndentingPrintWriter radioPw = new IndentingPrintWriter(pw);
+ android.util.IndentingPrintWriter radioPw = new android.util.IndentingPrintWriter(pw);
radioPw.printf("BroadcastRadioService\n");
radioPw.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioLogger.java b/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java
similarity index 65%
rename from services/core/java/com/android/server/broadcastradio/aidl/RadioLogger.java
rename to services/core/java/com/android/server/broadcastradio/RadioEventLogger.java
index cca351b..2c8f499 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioLogger.java
+++ b/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java
@@ -14,31 +14,35 @@
* limitations under the License.
*/
-package com.android.server.broadcastradio.aidl;
+package com.android.server.broadcastradio;
import android.text.TextUtils;
-import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Log;
import com.android.server.utils.Slogf;
/**
- * Event logger to log and dump events of radio module and tuner session
- * for AIDL broadcast radio HAL
+ * Event logger to log and dump events of broadcast radio service client for HIDL and AIDL
+ * broadcast HAL.
*/
-final class RadioLogger {
+public final class RadioEventLogger {
private final String mTag;
private final boolean mDebug;
private final LocalLog mEventLogger;
- RadioLogger(String tag, int loggerQueueSize) {
+ public RadioEventLogger(String tag, int loggerQueueSize) {
mTag = tag;
mDebug = Log.isLoggable(mTag, Log.DEBUG);
mEventLogger = new LocalLog(loggerQueueSize);
}
- void logRadioEvent(String logFormat, Object... args) {
+ /**
+ * Log broadcast radio service event
+ * @param logFormat String format of log message
+ * @param args Arguments of log message
+ */
+ public void logRadioEvent(String logFormat, Object... args) {
String log = TextUtils.formatSimple(logFormat, args);
mEventLogger.log(log);
if (mDebug) {
@@ -46,7 +50,11 @@
}
}
- void dump(IndentingPrintWriter pw) {
+ /**
+ * Dump broadcast radio service event
+ * @param pw Indenting print writer for dump
+ */
+ public void dump(android.util.IndentingPrintWriter pw) {
mEventLogger.dump(pw);
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
index b618aa3..9654a93 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
@@ -22,7 +22,6 @@
import android.hardware.radio.ICloseHandle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -94,7 +93,7 @@
if (mCloseHandle != null) mCloseHandle.close();
}
- public void dumpInfo(IndentingPrintWriter pw) {
+ public void dumpInfo(android.util.IndentingPrintWriter pw) {
pw.printf("ModuleWatcher:\n");
pw.increaseIndent();
@@ -192,7 +191,8 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
- IndentingPrintWriter announcementPrintWriter = new IndentingPrintWriter(printWriter);
+ android.util.IndentingPrintWriter announcementPrintWriter =
+ new android.util.IndentingPrintWriter(printWriter);
announcementPrintWriter.printf("AnnouncementAggregator\n");
announcementPrintWriter.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
index 086f3aa..1c42161 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
@@ -29,7 +29,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;
-import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.SparseArray;
@@ -261,7 +260,7 @@
*
* @param pw The file to which {@link BroadcastRadioServiceImpl} state is dumped.
*/
- public void dumpInfo(IndentingPrintWriter pw) {
+ public void dumpInfo(android.util.IndentingPrintWriter pw) {
synchronized (mLock) {
pw.printf("Next module id available: %d\n", mNextModuleId);
pw.printf("ServiceName to module id map:\n");
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index cd86510..0cac356 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -38,10 +38,10 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
-import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.broadcastradio.RadioEventLogger;
import com.android.server.broadcastradio.RadioServiceUserController;
import com.android.server.utils.Slogf;
@@ -59,7 +59,7 @@
private final Object mLock = new Object();
private final Handler mHandler;
- private final RadioLogger mLogger;
+ private final RadioEventLogger mLogger;
private final RadioManager.ModuleProperties mProperties;
/**
@@ -197,7 +197,7 @@
mProperties = Objects.requireNonNull(properties, "properties cannot be null");
mService = Objects.requireNonNull(service, "service cannot be null");
mHandler = new Handler(Looper.getMainLooper());
- mLogger = new RadioLogger(TAG, RADIO_EVENT_LOGGER_QUEUE_SIZE);
+ mLogger = new RadioEventLogger(TAG, RADIO_EVENT_LOGGER_QUEUE_SIZE);
}
@Nullable
@@ -524,7 +524,7 @@
return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
}
- void dumpInfo(IndentingPrintWriter pw) {
+ void dumpInfo(android.util.IndentingPrintWriter pw) {
pw.printf("RadioModule\n");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
index 4ed36ec..925f149 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
@@ -29,9 +29,9 @@
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.broadcastradio.RadioEventLogger;
import com.android.server.broadcastradio.RadioServiceUserController;
import com.android.server.utils.Slogf;
@@ -45,7 +45,7 @@
private final Object mLock = new Object();
- private final RadioLogger mLogger;
+ private final RadioEventLogger mLogger;
private final RadioModule mModule;
final int mUserId;
final android.hardware.radio.ITunerCallback mCallback;
@@ -70,7 +70,7 @@
mUserId = Binder.getCallingUserHandle().getIdentifier();
mCallback = Objects.requireNonNull(callback, "callback cannot be null");
mUid = Binder.getCallingUid();
- mLogger = new RadioLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
+ mLogger = new RadioEventLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
}
@Override
@@ -434,7 +434,7 @@
}
}
- void dumpInfo(IndentingPrintWriter pw) {
+ void dumpInfo(android.util.IndentingPrintWriter pw) {
pw.printf("TunerSession\n");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 3198842..e1650c2 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -30,7 +30,6 @@
import android.os.IHwBinder.DeathRecipient;
import android.os.RemoteException;
import android.util.ArrayMap;
-import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -222,7 +221,7 @@
*
* @param pw The file to which BroadcastRadioService state is dumped.
*/
- public void dumpInfo(IndentingPrintWriter pw) {
+ public void dumpInfo(android.util.IndentingPrintWriter pw) {
synchronized (mLock) {
pw.printf("Next module id available: %d\n", mNextModuleId);
pw.printf("ServiceName to module id map:\n");
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioEventLogger.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioEventLogger.java
deleted file mode 100644
index b8d1228..0000000
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioEventLogger.java
+++ /dev/null
@@ -1,46 +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.server.broadcastradio.hal2;
-
-import android.util.IndentingPrintWriter;
-import android.util.LocalLog;
-import android.util.Log;
-
-import com.android.server.utils.Slogf;
-
-final class RadioEventLogger {
- private final String mTag;
- private final LocalLog mEventLogger;
-
- RadioEventLogger(String tag, int loggerQueueSize) {
- mTag = tag;
- mEventLogger = new LocalLog(loggerQueueSize);
- }
-
- @SuppressWarnings("AnnotateFormatMethod")
- void logRadioEvent(String logFormat, Object... args) {
- String log = String.format(logFormat, args);
- mEventLogger.log(log);
- if (Log.isLoggable(mTag, Log.DEBUG)) {
- Slogf.d(mTag, log);
- }
- }
-
- void dump(IndentingPrintWriter pw) {
- mEventLogger.dump(pw);
- }
-}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 0e11df8..7269f24 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -40,11 +40,11 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
-import android.util.IndentingPrintWriter;
import android.util.MutableInt;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.broadcastradio.RadioEventLogger;
import com.android.server.broadcastradio.RadioServiceUserController;
import com.android.server.utils.Slogf;
@@ -453,7 +453,7 @@
return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
}
- void dumpInfo(IndentingPrintWriter pw) {
+ void dumpInfo(android.util.IndentingPrintWriter pw) {
pw.printf("RadioModule\n");
pw.increaseIndent();
pw.printf("BroadcastRadioService: %s\n", mService);
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 6d435e3..b1b5d34 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -31,11 +31,11 @@
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.IndentingPrintWriter;
import android.util.MutableBoolean;
import android.util.MutableInt;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.broadcastradio.RadioEventLogger;
import com.android.server.broadcastradio.RadioServiceUserController;
import com.android.server.utils.Slogf;
@@ -389,7 +389,7 @@
}
}
- void dumpInfo(IndentingPrintWriter pw) {
+ void dumpInfo(android.util.IndentingPrintWriter pw) {
pw.printf("TunerSession\n");
pw.increaseIndent();
pw.printf("HIDL HAL Session: %s\n", mHwSession);
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 05e681e..645a366 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -61,6 +61,9 @@
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -69,6 +72,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Range;
import android.util.Slog;
import android.view.Display;
import android.view.IDisplayWindowListener;
@@ -85,6 +89,8 @@
import com.android.server.SystemService;
import com.android.server.wm.WindowManagerInternal;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -243,6 +249,7 @@
public int mVideoStabilizationMode;
public boolean mUsedUltraWide;
public boolean mUsedZoomOverride;
+ public Range<Integer> mMostRequestedFpsRange;
public final long mLogId;
public final int mSessionIndex;
@@ -265,13 +272,15 @@
mDeviceError = deviceError;
mLogId = logId;
mSessionIndex = sessionIdx;
+ mMostRequestedFpsRange = new Range<Integer>(0, 0);
}
public void markCompleted(int internalReconfigure, long requestCount,
long resultErrorCount, boolean deviceError,
List<CameraStreamStats> streamStats, String userTag,
int videoStabilizationMode, boolean usedUltraWide,
- boolean usedZoomOverride, CameraExtensionSessionStats extStats) {
+ boolean usedZoomOverride, Range<Integer> mostRequestedFpsRange,
+ CameraExtensionSessionStats extStats) {
if (mCompleted) {
return;
}
@@ -287,6 +296,7 @@
mUsedUltraWide = usedUltraWide;
mUsedZoomOverride = usedZoomOverride;
mExtSessionStats = extStats;
+ mMostRequestedFpsRange = mostRequestedFpsRange;
if (CameraServiceProxy.DEBUG) {
Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) +
" was in use by " + mClientName + " for " +
@@ -637,6 +647,60 @@
Binder.restoreCallingIdentity(ident);
}
}
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver)
+ throws RemoteException {
+ new CSPShellCmd(CameraServiceProxy.this)
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ private static class CSPShellCmd extends ShellCommand {
+ private static final String TAG = "CSPShellCmd";
+ private static final String USAGE = """
+ usage: cmd media.camera.proxy SUBCMD [args]
+
+ SUBCMDs:
+ dump_events: Write out all collected camera usage events to statsd.
+ Does not print to terminal.
+ help: You're reading it.
+ """;
+
+ private final CameraServiceProxy mCameraServiceProxy;
+
+ CSPShellCmd(CameraServiceProxy proxy) {
+ mCameraServiceProxy = proxy;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ switch (cmd.replace('-', '_')) {
+ case "dump_events":
+ int eventCount = mCameraServiceProxy.getUsageEventCount();
+ mCameraServiceProxy.dumpUsageEvents();
+ pw.println("Camera usage events dumped: " + eventCount);
+ break;
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (Exception e) {
+ Slog.e(mCameraServiceProxy.TAG, "Error running shell command", e);
+ return 1;
+ }
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ getOutPrintWriter().println(USAGE);
+ }
+ }
};
private final FoldStateListener mFoldStateListener;
@@ -882,6 +946,9 @@
? ", zoomOverrideUsage " + e.mUsedZoomOverride
: "";
+ String mostRequestedFpsRangeDebug = Flags.analytics24q3()
+ ? ", mostRequestedFpsRange " + e.mMostRequestedFpsRange
+ : "";
Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction
+ " clientName " + e.mClientName
+ ", duration " + e.getDuration()
@@ -900,6 +967,7 @@
+ ", videoStabilizationMode " + e.mVideoStabilizationMode
+ ultrawideDebug
+ zoomOverrideDebug
+ + mostRequestedFpsRangeDebug
+ ", logId " + e.mLogId
+ ", sessionIndex " + e.mSessionIndex
+ ", mExtSessionStats {type " + extensionType
@@ -966,7 +1034,17 @@
e.mUserTag, e.mVideoStabilizationMode,
e.mLogId, e.mSessionIndex,
extensionType, extensionIsAdvanced, e.mUsedUltraWide,
- e.mUsedZoomOverride);
+ e.mUsedZoomOverride,
+ e.mMostRequestedFpsRange.getLower(), e.mMostRequestedFpsRange.getUpper());
+ }
+ }
+
+ /**
+ * Get camera usage event count
+ */
+ int getUsageEventCount() {
+ synchronized (mLock) {
+ return mCameraUsageHistory.size();
}
}
@@ -1173,6 +1251,10 @@
long logId = cameraState.getLogId();
int sessionIdx = cameraState.getSessionIndex();
CameraExtensionSessionStats extSessionStats = cameraState.getExtensionSessionStats();
+ Range<Integer> mostRequestedFpsRange = Flags.analytics24q3()
+ ? cameraState.getMostRequestedFpsRange()
+ : new Range<Integer>(0, 0);
+
synchronized(mLock) {
// Update active camera list and notify NFC if necessary
boolean wasEmpty = mActiveCameraUsage.isEmpty();
@@ -1228,7 +1310,8 @@
oldEvent.markCompleted(/*internalReconfigure*/0, /*requestCount*/0,
/*resultErrorCount*/0, /*deviceError*/false, streamStats,
/*userTag*/"", /*videoStabilizationMode*/-1, /*usedUltraWide*/false,
- /*usedZoomOverride*/false, new CameraExtensionSessionStats());
+ /*usedZoomOverride*/false, new Range<Integer>(0, 0),
+ new CameraExtensionSessionStats());
mCameraUsageHistory.add(oldEvent);
}
break;
@@ -1240,7 +1323,7 @@
doneEvent.markCompleted(internalReconfigureCount, requestCount,
resultErrorCount, deviceError, streamStats, userTag,
videoStabilizationMode, usedUltraWide, usedZoomOverride,
- extSessionStats);
+ mostRequestedFpsRange, extSessionStats);
mCameraUsageHistory.add(doneEvent);
// Do not double count device error
deviceError = false;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 85a231f..5c93181 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -585,7 +585,7 @@
* </point>
* </luxThresholds>
* </idleScreenRefreshRateTimeout>
- *
+ * <supportsVrr>true</supportsVrr>
*
* </displayConfiguration>
* }
@@ -872,6 +872,8 @@
*/
private float mBrightnessCapForWearBedtimeMode;
+ private boolean mVrrSupportEnabled;
+
private final DisplayManagerFlags mFlags;
@VisibleForTesting
@@ -1606,6 +1608,13 @@
return mBrightnessCapForWearBedtimeMode;
}
+ /**
+ * @return true if display supports dvrr
+ */
+ public boolean isVrrSupportEnabled() {
+ return mVrrSupportEnabled;
+ }
+
@Override
public String toString() {
return "DisplayDeviceConfig{"
@@ -1705,6 +1714,8 @@
+ "\n"
+ "mEvenDimmerBrightnessData:" + (mEvenDimmerBrightnessData != null
? mEvenDimmerBrightnessData.toString() : "null")
+ + "\n"
+ + "mVrrSupported= " + mVrrSupportEnabled + "\n"
+ "}";
}
@@ -1755,7 +1766,8 @@
loadDensityMapping(config);
loadBrightnessDefaultFromDdcXml(config);
loadBrightnessConstraintsFromConfigXml();
- if (mFlags.isEvenDimmerEnabled()) {
+ if (mFlags.isEvenDimmerEnabled() && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_evenDimmerEnabled)) {
mEvenDimmerBrightnessData = EvenDimmerBrightnessData.loadConfig(config);
}
loadBrightnessMap(config);
@@ -1779,6 +1791,7 @@
mHdrBrightnessData = HdrBrightnessData.loadConfig(config);
loadBrightnessCapForWearBedtimeMode(config);
loadIdleScreenRefreshRateTimeoutConfigs(config);
+ mVrrSupportEnabled = config.getSupportsVrr();
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 31092f2..68e2bd6 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -548,6 +548,17 @@
}
};
+ private final DisplayModeDirector.DisplayDeviceConfigProvider mDisplayDeviceConfigProvider =
+ displayId -> {
+ synchronized (mSyncRoot) {
+ final DisplayDevice device = getDeviceForDisplayLocked(displayId);
+ if (device == null) {
+ return null;
+ }
+ return device.getDisplayDeviceConfig();
+ }
+ };
+
private final BrightnessSynchronizer mBrightnessSynchronizer;
private final DeviceConfigParameterProvider mConfigParameterProvider;
@@ -599,7 +610,8 @@
mLogicalDisplayMapper = new LogicalDisplayMapper(mContext,
foldSettingProvider, new FoldGracePeriodProvider(),
mDisplayDeviceRepo, new LogicalDisplayListener(), mSyncRoot, mHandler, mFlags);
- mDisplayModeDirector = new DisplayModeDirector(context, mHandler, mFlags);
+ mDisplayModeDirector = new DisplayModeDirector(
+ context, mHandler, mFlags, mDisplayDeviceConfigProvider);
mBrightnessSynchronizer = new BrightnessSynchronizer(mContext,
mFlags.isBrightnessIntRangeUserPerceptionEnabled());
Resources resources = mContext.getResources();
@@ -2764,6 +2776,7 @@
+ requestedRefreshRate + " on Display: " + displayId);
}
}
+
mDisplayModeDirector.getAppRequestObserver().setAppRequest(
displayId, requestedModeId, requestedMinRefreshRate, requestedMaxRefreshRate);
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index d084d1c..fa42316 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -105,6 +105,7 @@
* picked by the system based on system-wide and display-specific configuration.
*/
public class DisplayModeDirector {
+
public static final float SYNCHRONIZED_REFRESH_RATE_TARGET = DEFAULT_LOW_REFRESH_RATE;
public static final float SYNCHRONIZED_REFRESH_RATE_TOLERANCE = 1;
private static final String TAG = "DisplayModeDirector";
@@ -119,8 +120,6 @@
private static final int MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED = 7;
private static final int MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED = 8;
- private static final float FLOAT_TOLERANCE = RefreshRateRange.FLOAT_TOLERANCE;
-
private final Object mLock = new Object();
private final Context mContext;
@@ -148,6 +147,8 @@
private SparseArray<Display.Mode[]> mSupportedModesByDisplay;
// A map from the display ID to the default mode of that display.
private SparseArray<Display.Mode> mDefaultModeByDisplay;
+ // a map from display id to display device config
+ private SparseArray<DisplayDeviceConfig> mDisplayDeviceConfigByDisplay = new SparseArray<>();
private BrightnessObserver mBrightnessObserver;
@@ -189,19 +190,19 @@
private final DisplayManagerFlags mDisplayManagerFlags;
- private final boolean mDvrrSupported;
-
+ private final DisplayDeviceConfigProvider mDisplayDeviceConfigProvider;
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
- @NonNull DisplayManagerFlags displayManagerFlags) {
- this(context, handler, new RealInjector(context), displayManagerFlags);
+ @NonNull DisplayManagerFlags displayManagerFlags,
+ @NonNull DisplayDeviceConfigProvider displayDeviceConfigProvider) {
+ this(context, handler, new RealInjector(context),
+ displayManagerFlags, displayDeviceConfigProvider);
}
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
@NonNull Injector injector,
- @NonNull DisplayManagerFlags displayManagerFlags) {
- mDvrrSupported = context.getResources().getBoolean(
- com.android.internal.R.bool.config_supportsDvrr);
+ @NonNull DisplayManagerFlags displayManagerFlags,
+ @NonNull DisplayDeviceConfigProvider displayDeviceConfigProvider) {
mIsDisplayResolutionRangeVotingEnabled = displayManagerFlags
.isDisplayResolutionRangeVotingEnabled();
mIsUserPreferredModeVoteEnabled = displayManagerFlags.isUserPreferredModeVoteEnabled();
@@ -212,6 +213,7 @@
mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled = displayManagerFlags
.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled();
mDisplayManagerFlags = displayManagerFlags;
+ mDisplayDeviceConfigProvider = displayDeviceConfigProvider;
mContext = context;
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
mInjector = injector;
@@ -222,20 +224,19 @@
mAppRequestObserver = new AppRequestObserver();
mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
- mSettingsObserver = new SettingsObserver(context, handler, mDvrrSupported,
- displayManagerFlags);
- mBrightnessObserver = new BrightnessObserver(context, handler, injector, mDvrrSupported,
+ mSettingsObserver = new SettingsObserver(context, handler, displayManagerFlags);
+ mBrightnessObserver = new BrightnessObserver(context, handler, injector,
displayManagerFlags);
mDefaultDisplayDeviceConfig = null;
mUdfpsObserver = new UdfpsObserver();
mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked,
mVotesStatsReporter);
- mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage);
+ mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage, injector);
mSensorObserver = new ProximitySensorObserver(mVotesStorage, injector);
mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, mVotesStorage);
mHbmObserver = new HbmObserver(injector, mVotesStorage, BackgroundThread.getHandler(),
mDeviceConfigDisplaySettings);
- if (mDvrrSupported && displayManagerFlags.isRestrictDisplayModesEnabled()) {
+ if (displayManagerFlags.isRestrictDisplayModesEnabled()) {
mSystemRequestObserver = new SystemRequestObserver(mVotesStorage);
} else {
mSystemRequestObserver = null;
@@ -315,7 +316,8 @@
List<Display.Mode> availableModes = new ArrayList<>();
availableModes.add(defaultMode);
VoteSummary primarySummary = new VoteSummary(mIsDisplayResolutionRangeVotingEnabled,
- mDvrrSupported, mLoggingEnabled, mSupportsFrameRateOverride);
+ isVrrSupportedLocked(displayId),
+ mLoggingEnabled, mSupportsFrameRateOverride);
int lowestConsideredPriority = Vote.MIN_PRIORITY;
int highestConsideredPriority = Vote.MAX_PRIORITY;
@@ -355,7 +357,8 @@
}
VoteSummary appRequestSummary = new VoteSummary(mIsDisplayResolutionRangeVotingEnabled,
- mDvrrSupported, mLoggingEnabled, mSupportsFrameRateOverride);
+ isVrrSupportedLocked(displayId),
+ mLoggingEnabled, mSupportsFrameRateOverride);
appRequestSummary.applyVotes(votes,
Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF,
@@ -442,6 +445,20 @@
return mAppRequestObserver;
}
+ private boolean isVrrSupportedLocked(int displayId) {
+ DisplayDeviceConfig config = mDisplayDeviceConfigByDisplay.get(displayId);
+ return config != null && config.isVrrSupportEnabled();
+ }
+
+ private boolean isVrrSupportedByAnyDisplayLocked() {
+ for (int i = 0; i < mDisplayDeviceConfigByDisplay.size(); i++) {
+ if (mDisplayDeviceConfigByDisplay.valueAt(i).isVrrSupportEnabled()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate ranges.
*/
@@ -539,7 +556,13 @@
*/
public void requestDisplayModes(IBinder token, int displayId, int[] modeIds) {
if (mSystemRequestObserver != null) {
- mSystemRequestObserver.requestDisplayModes(token, displayId, modeIds);
+ boolean vrrSupported;
+ synchronized (mLock) {
+ vrrSupported = isVrrSupportedLocked(displayId);
+ }
+ if (vrrSupported) {
+ mSystemRequestObserver.requestDisplayModes(token, displayId, modeIds);
+ }
}
}
@@ -627,6 +650,11 @@
}
@VisibleForTesting
+ void injectDisplayDeviceConfigByDisplay(SparseArray<DisplayDeviceConfig> ddcByDisplay) {
+ mDisplayDeviceConfigByDisplay = ddcByDisplay;
+ }
+
+ @VisibleForTesting
void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) {
mVotesStorage.injectVotesByDisplay(votesByDisplay);
}
@@ -672,6 +700,16 @@
}
/**
+ * Provides access to DisplayDeviceConfig for specific display
+ */
+ public interface DisplayDeviceConfigProvider {
+ /**
+ * Returns DisplayDeviceConfig for specific display
+ */
+ @Nullable DisplayDeviceConfig getDisplayDeviceConfig(int displayId);
+ }
+
+ /**
* Listens for changes refresh rate coordination.
*/
public interface DesiredDisplayModeSpecsListener {
@@ -906,11 +944,11 @@
private float mDefaultPeakRefreshRate;
private float mDefaultRefreshRate;
- SettingsObserver(@NonNull Context context, @NonNull Handler handler, boolean dvrrSupported,
+ SettingsObserver(@NonNull Context context, @NonNull Handler handler,
DisplayManagerFlags flags) {
super(handler);
mContext = context;
- mVsynLowPowerVoteEnabled = dvrrSupported && flags.isVsyncLowPowerVoteEnabled();
+ mVsynLowPowerVoteEnabled = flags.isVsyncLowPowerVoteEnabled();
// We don't want to load from the DeviceConfig while constructing since this leads to
// a spike in the latency of DisplayManagerService startup. This happens because
// reading from the DeviceConfig is an intensive IO operation and having it in the
@@ -1020,7 +1058,7 @@
boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
final Vote vote;
- if (inLowPowerMode && mVsynLowPowerVoteEnabled) {
+ if (inLowPowerMode && mVsynLowPowerVoteEnabled && isVrrSupportedByAnyDisplayLocked()) {
vote = Vote.forSupportedRefreshRates(List.of(
new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
/* vsyncRate= */ 240f),
@@ -1065,20 +1103,6 @@
if (Float.isInfinite(minRefreshRate)) {
// Infinity means that we want the highest possible refresh rate
minRefreshRate = highestRefreshRate;
-
- if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled
- && displayId == Display.DEFAULT_DISPLAY) {
- // The flag has been turned off, we need to restore the original value. We'll
- // use the peak refresh rate of the default display.
- Settings.System.putFloatForUser(cr, Settings.System.MIN_REFRESH_RATE,
- highestRefreshRate, cr.getUserId());
- }
- } else if (mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled
- && displayId == Display.DEFAULT_DISPLAY
- && Math.round(minRefreshRate) == Math.round(highestRefreshRate)) {
- // The flag has been turned on, we need to upgrade the setting
- Settings.System.putFloatForUser(cr, Settings.System.MIN_REFRESH_RATE,
- Float.POSITIVE_INFINITY, cr.getUserId());
}
float peakRefreshRate = Settings.System.getFloatForUser(cr,
@@ -1086,20 +1110,6 @@
if (Float.isInfinite(peakRefreshRate)) {
// Infinity means that we want the highest possible refresh rate
peakRefreshRate = highestRefreshRate;
-
- if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled
- && displayId == Display.DEFAULT_DISPLAY) {
- // The flag has been turned off, we need to restore the original value. We'll
- // use the peak refresh rate of the default display.
- Settings.System.putFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE,
- highestRefreshRate, cr.getUserId());
- }
- } else if (mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled
- && displayId == Display.DEFAULT_DISPLAY
- && Math.round(peakRefreshRate) == Math.round(highestRefreshRate)) {
- // The flag has been turned on, we need to upgrade the setting
- Settings.System.putFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE,
- Float.POSITIVE_INFINITY, cr.getUserId());
}
updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate,
@@ -1190,8 +1200,8 @@
/**
* Sets refresh rates from app request
*/
- public void setAppRequest(int displayId, int modeId, float requestedMinRefreshRateRange,
- float requestedMaxRefreshRateRange) {
+ public void setAppRequest(int displayId, int modeId,
+ float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {
synchronized (mLock) {
setAppRequestedModeLocked(displayId, modeId);
setAppPreferredRefreshRateRangeLocked(displayId, requestedMinRefreshRateRange,
@@ -1204,7 +1214,6 @@
if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
return;
}
-
final Vote baseModeRefreshRateVote;
final Vote sizeVote;
if (requestedMode != null) {
@@ -1295,13 +1304,15 @@
private final Context mContext;
private final Handler mHandler;
private final VotesStorage mVotesStorage;
+
private int mExternalDisplayPeakWidth;
private int mExternalDisplayPeakHeight;
private int mExternalDisplayPeakRefreshRate;
private final boolean mRefreshRateSynchronizationEnabled;
private final Set<Integer> mExternalDisplaysConnected = new HashSet<>();
- DisplayObserver(Context context, Handler handler, VotesStorage votesStorage) {
+ DisplayObserver(Context context, Handler handler, VotesStorage votesStorage,
+ Injector injector) {
mContext = context;
mHandler = handler;
mVotesStorage = votesStorage;
@@ -1342,17 +1353,21 @@
modes.put(displayId, info.supportedModes);
defaultModes.put(displayId, info.getDefaultMode());
}
+ DisplayDeviceConfig defaultDisplayConfig = mDisplayDeviceConfigProvider
+ .getDisplayDeviceConfig(Display.DEFAULT_DISPLAY);
synchronized (mLock) {
final int size = modes.size();
for (int i = 0; i < size; i++) {
mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i));
mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i));
}
+ mDisplayDeviceConfigByDisplay.put(Display.DEFAULT_DISPLAY, defaultDisplayConfig);
}
}
@Override
public void onDisplayAdded(int displayId) {
+ updateDisplayDeviceConfig(displayId);
DisplayInfo displayInfo = getDisplayInfo(displayId);
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
@@ -1366,6 +1381,7 @@
synchronized (mLock) {
mSupportedModesByDisplay.remove(displayId);
mDefaultModeByDisplay.remove(displayId);
+ mDisplayDeviceConfigByDisplay.remove(displayId);
mSettingsObserver.removeRefreshRateSetting(displayId);
}
updateLayoutLimitedFrameRate(displayId, null);
@@ -1376,6 +1392,7 @@
@Override
public void onDisplayChanged(int displayId) {
+ updateDisplayDeviceConfig(displayId);
DisplayInfo displayInfo = getDisplayInfo(displayId);
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
@@ -1505,6 +1522,14 @@
mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE, null);
}
+ private void updateDisplayDeviceConfig(int displayId) {
+ DisplayDeviceConfig config = mDisplayDeviceConfigProvider
+ .getDisplayDeviceConfig(displayId);
+ synchronized (mLock) {
+ mDisplayDeviceConfigByDisplay.put(displayId, config);
+ }
+ }
+
private void updateDisplayModes(int displayId, @Nullable DisplayInfo info) {
if (info == null) {
return;
@@ -1631,7 +1656,7 @@
private @Temperature.ThrottlingStatus int mThermalStatus = Temperature.THROTTLING_NONE;
BrightnessObserver(Context context, Handler handler, Injector injector,
- boolean dvrrSupported , DisplayManagerFlags flags) {
+ DisplayManagerFlags flags) {
mContext = context;
mHandler = handler;
mInjector = injector;
@@ -1639,7 +1664,7 @@
/* attemptReadFromFeatureParams= */ false);
mRefreshRateInHighZone = context.getResources().getInteger(
R.integer.config_fixedRefreshRateInHighZone);
- mVsyncLowLightBlockingVoteEnabled = dvrrSupported && flags.isVsyncLowLightVoteEnabled();
+ mVsyncLowLightBlockingVoteEnabled = flags.isVsyncLowLightVoteEnabled();
}
/**
@@ -2225,7 +2250,8 @@
}
}
- if (mVsyncLowLightBlockingVoteEnabled) {
+ if (mVsyncLowLightBlockingVoteEnabled
+ && isVrrSupportedLocked(Display.DEFAULT_DISPLAY)) {
refreshRateSwitchingVote = Vote.forSupportedRefreshRatesAndDisableSwitching(
List.of(
new SupportedRefreshRatesVote.RefreshRates(
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index f383679..47f73f1 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -167,7 +167,8 @@
public void onBootPhase(int phase) {
final int latestFontLoadBootPhase =
(Flags.completeFontLoadInSystemServicesReady())
- ? SystemService.PHASE_SYSTEM_SERVICES_READY
+ // Complete font load in the phase before PHASE_SYSTEM_SERVICES_READY
+ ? SystemService.PHASE_LOCK_SETTINGS_READY
: SystemService.PHASE_ACTIVITY_MANAGER_READY;
if (phase == latestFontLoadBootPhase) {
// Wait for FontManagerService to start since it will be needed after this point.
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index f32c11d..73df594 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -250,8 +250,19 @@
private final Object mAssociationsLock = new Object();
@GuardedBy("mAssociationsLock")
private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<>();
+
+ // The associations of input devices to displays by port. Maps from {InputDevice#mName} (String)
+ // to {DisplayInfo#uniqueId} (String) so that events from the Input Device go to a
+ // specific display.
@GuardedBy("mAssociationsLock")
- private final Map<String, String> mUniqueIdAssociations = new ArrayMap<>();
+ private final Map<String, String> mUniqueIdAssociationsByPort = new ArrayMap<>();
+
+ // The associations of input devices to displays by descriptor. Maps from
+ // {InputDevice#mDescriptor} to {DisplayInfo#uniqueId} (String) so that events from the
+ // input device go to a specific display.
+ @GuardedBy("mAssociationsLock")
+ private final Map<String, String> mUniqueIdAssociationsByDescriptor = new ArrayMap<>();
+
// The map from input port (String) to the keyboard layout identifiers (comma separated string
// containing language tag and layout type) associated with the corresponding keyboard device.
// Currently only accessed by InputReader.
@@ -1741,8 +1752,8 @@
/**
* Add a runtime association between the input port and the display port. This overrides any
* static associations.
- * @param inputPort The port of the input device.
- * @param displayPort The physical port of the associated display.
+ * @param inputPort the port of the input device
+ * @param displayPort the physical port of the associated display
*/
@Override // Binder call
public void addPortAssociation(@NonNull String inputPort, int displayPort) {
@@ -1763,7 +1774,7 @@
/**
* Remove the runtime association between the input port and the display port. Any existing
* static association for the cleared input port will be restored.
- * @param inputPort The port of the input device to be cleared.
+ * @param inputPort the port of the input device to be cleared
*/
@Override // Binder call
public void removePortAssociation(@NonNull String inputPort) {
@@ -1782,10 +1793,11 @@
}
@Override // Binder call
- public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) {
+ public void addUniqueIdAssociationByPort(@NonNull String inputPort,
+ @NonNull String displayUniqueId) {
if (!checkCallingPermission(
android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
- "addUniqueIdAssociation()")) {
+ "addUniqueIdAssociationByPort()")) {
throw new SecurityException(
"Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
}
@@ -1793,22 +1805,65 @@
Objects.requireNonNull(inputPort);
Objects.requireNonNull(displayUniqueId);
synchronized (mAssociationsLock) {
- mUniqueIdAssociations.put(inputPort, displayUniqueId);
+ mUniqueIdAssociationsByPort.put(inputPort, displayUniqueId);
}
mNative.changeUniqueIdAssociation();
}
@Override // Binder call
- public void removeUniqueIdAssociation(@NonNull String inputPort) {
+ public void removeUniqueIdAssociationByPort(@NonNull String inputPort) {
if (!checkCallingPermission(
android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
- "removeUniqueIdAssociation()")) {
+ "removeUniqueIdAssociationByPort()")) {
throw new SecurityException("Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
}
Objects.requireNonNull(inputPort);
synchronized (mAssociationsLock) {
- mUniqueIdAssociations.remove(inputPort);
+ mUniqueIdAssociationsByPort.remove(inputPort);
+ }
+ mNative.changeUniqueIdAssociation();
+ }
+
+ /**
+ * Adds a runtime association between the input device descriptor and the display unique id.
+ * @param inputDeviceDescriptor the descriptor of the input device
+ * @param displayUniqueId the unique ID of the display
+ */
+ @Override // Binder call
+ public void addUniqueIdAssociationByDescriptor(@NonNull String inputDeviceDescriptor,
+ @NonNull String displayUniqueId) {
+ if (!checkCallingPermission(
+ android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
+ "addUniqueIdAssociationByDescriptor()")) {
+ throw new SecurityException(
+ "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
+ }
+
+ Objects.requireNonNull(inputDeviceDescriptor);
+ Objects.requireNonNull(displayUniqueId);
+ synchronized (mAssociationsLock) {
+ mUniqueIdAssociationsByDescriptor.put(inputDeviceDescriptor, displayUniqueId);
+ }
+ mNative.changeUniqueIdAssociation();
+ }
+
+ /**
+ * Removes the runtime association between the input device and the display.
+ * @param inputDeviceDescriptor the descriptor of the input device
+ */
+ @Override // Binder call
+ public void removeUniqueIdAssociationByDescriptor(@NonNull String inputDeviceDescriptor) {
+ if (!checkCallingPermission(
+ android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
+ "removeUniqueIdAssociationByDescriptor()")) {
+ throw new SecurityException(
+ "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
+ }
+
+ Objects.requireNonNull(inputDeviceDescriptor);
+ synchronized (mAssociationsLock) {
+ mUniqueIdAssociationsByDescriptor.remove(inputDeviceDescriptor);
}
mNative.changeUniqueIdAssociation();
}
@@ -2183,13 +2238,20 @@
pw.println(" display: " + v);
});
}
- if (!mUniqueIdAssociations.isEmpty()) {
+ if (!mUniqueIdAssociationsByPort.isEmpty()) {
pw.println("Unique Id Associations:");
- mUniqueIdAssociations.forEach((k, v) -> {
+ mUniqueIdAssociationsByPort.forEach((k, v) -> {
pw.print(" port: " + k);
pw.println(" uniqueId: " + v);
});
}
+ if (!mUniqueIdAssociationsByDescriptor.isEmpty()) {
+ pw.println("Unique Id Associations:");
+ mUniqueIdAssociationsByDescriptor.forEach((k, v) -> {
+ pw.print(" descriptor: " + k);
+ pw.println(" uniqueId: " + v);
+ });
+ }
if (!mDeviceTypeAssociations.isEmpty()) {
pw.println("Type Associations:");
mDeviceTypeAssociations.forEach((k, v) -> {
@@ -2622,10 +2684,21 @@
// Native callback
@SuppressWarnings("unused")
- private String[] getInputUniqueIdAssociations() {
+ private String[] getInputUniqueIdAssociationsByPort() {
final Map<String, String> associations;
synchronized (mAssociationsLock) {
- associations = new HashMap<>(mUniqueIdAssociations);
+ associations = new HashMap<>(mUniqueIdAssociationsByPort);
+ }
+
+ return flatten(associations);
+ }
+
+ // Native callback
+ @SuppressWarnings("unused")
+ private String[] getInputUniqueIdAssociationsByDescriptor() {
+ final Map<String, String> associations;
+ synchronized (mAssociationsLock) {
+ associations = new HashMap<>(mUniqueIdAssociationsByDescriptor);
}
return flatten(associations);
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 9ba647f..97c32b9 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -664,6 +664,46 @@
}
}
+ /**
+ * <ol>
+ * <li> Layout selection Algorithm:
+ * <ul>
+ * <li> Choose product specific layout(KCM file with matching vendor ID and product
+ * ID) </li>
+ * <li> If none, then find layout based on PK layout info (based on country code
+ * provided by the HID descriptor of the keyboard) </li>
+ * <li> If none, then find layout based on IME layout info associated with the IME
+ * subtype </li>
+ * <li> If none, return null (Generic.kcm is the default) </li>
+ * </ul>
+ * </li>
+ * <li> Finding correct layout corresponding to provided layout info:
+ * <ul>
+ * <li> Filter all available layouts based on the IME subtype script code </li>
+ * <li> Derive locale from the provided layout info </li>
+ * <li> If layoutType i.e. qwerty, azerty, etc. is provided, filter layouts by
+ * layoutType and try to find matching layout to the derived locale. </li>
+ * <li> If none found or layoutType not provided, then ignore the layoutType and try
+ * to find matching layout to the derived locale. </li>
+ * </ul>
+ * </li>
+ * <li> Finding matching layout for the derived locale:
+ * <ul>
+ * <li> If language code doesn't match, ignore the layout (We can never match a
+ * layout if language code isn't matching) </li>
+ * <li> If country code matches, layout score +1 </li>
+ * <li> Else if country code of layout is empty, layout score +0.5 (empty country
+ * code is a semi match with derived locale with country code, this is to prioritize
+ * empty country code layouts over fully mismatching layouts) </li>
+ * <li> If variant matches, layout score +1 </li>
+ * <li> Else if variant of layout is empty, layout score +0.5 (empty variant is a
+ * semi match with derive locale with country code, this is to prioritize empty
+ * variant layouts over fully mismatching layouts) </li>
+ * <li> Choose the layout with the best score. </li>
+ * </ul>
+ * </li>
+ * </ol>
+ */
@NonNull
private static KeyboardLayoutSelectionResult getDefaultKeyboardLayoutBasedOnImeInfo(
KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo,
@@ -753,8 +793,8 @@
private static String getMatchingLayoutForProvidedLanguageTag(List<KeyboardLayout> layoutList,
@NonNull String languageTag) {
Locale locale = Locale.forLanguageTag(languageTag);
- String layoutMatchingLanguage = null;
- String layoutMatchingLanguageAndCountry = null;
+ String bestMatchingLayout = null;
+ float bestMatchingLayoutScore = 0;
for (KeyboardLayout layout : layoutList) {
final LocaleList locales = layout.getLocales();
@@ -763,23 +803,28 @@
if (l == null) {
continue;
}
- if (l.getLanguage().equals(locale.getLanguage())) {
- if (layoutMatchingLanguage == null) {
- layoutMatchingLanguage = layout.getDescriptor();
- }
- if (l.getCountry().equals(locale.getCountry())) {
- if (layoutMatchingLanguageAndCountry == null) {
- layoutMatchingLanguageAndCountry = layout.getDescriptor();
- }
- if (l.getVariant().equals(locale.getVariant())) {
- return layout.getDescriptor();
- }
- }
+ if (!l.getLanguage().equals(locale.getLanguage())) {
+ // If language mismatches: NEVER choose that layout
+ continue;
+ }
+ float layoutScore = 1; // If language matches then score +1
+ if (l.getCountry().equals(locale.getCountry())) {
+ layoutScore += 1; // If country matches then score +1
+ } else if (TextUtils.isEmpty(l.getCountry())) {
+ layoutScore += 0.5; // Consider empty country as semi-match
+ }
+ if (l.getVariant().equals(locale.getVariant())) {
+ layoutScore += 1; // If variant matches then score +1
+ } else if (TextUtils.isEmpty(l.getVariant())) {
+ layoutScore += 0.5; // Consider empty variant as semi-match
+ }
+ if (layoutScore > bestMatchingLayoutScore) {
+ bestMatchingLayoutScore = layoutScore;
+ bestMatchingLayout = layout.getDescriptor();
}
}
}
- return layoutMatchingLanguageAndCountry != null
- ? layoutMatchingLanguageAndCountry : layoutMatchingLanguage;
+ return bestMatchingLayout;
}
private void reloadKeyboardLayouts() {
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
index c7b60da..dd6433d 100644
--- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
@@ -19,11 +19,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Handler;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
@@ -67,7 +69,7 @@
AdditionalSubtypeUtils.save(map, inputMethodMap, userId);
}
- static void initialize(@NonNull Handler handler) {
+ static void initialize(@NonNull Handler handler, @NonNull Context context) {
final UserManagerInternal userManagerInternal =
LocalServices.getService(UserManagerInternal.class);
handler.post(() -> {
@@ -79,8 +81,16 @@
handler.post(() -> {
synchronized (ImfLock.class) {
if (!sPerUserMap.contains(userId)) {
- sPerUserMap.put(userId,
- AdditionalSubtypeUtils.load(userId));
+ final AdditionalSubtypeMap additionalSubtypeMap =
+ AdditionalSubtypeUtils.load(userId);
+ sPerUserMap.put(userId, additionalSubtypeMap);
+ final InputMethodSettings settings =
+ InputMethodManagerService
+ .queryInputMethodServicesInternal(context,
+ userId,
+ additionalSubtypeMap,
+ DirectBootAwareness.AUTO);
+ InputMethodSettingsRepository.put(userId, settings);
}
}
});
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
new file mode 100644
index 0000000..7890fe0
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+import android.annotation.BinderThread;
+import android.annotation.EnforcePermission;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
+
+import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IBooleanListener;
+import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
+import com.android.internal.inputmethod.IImeTracker;
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+import com.android.internal.inputmethod.InputBindResult;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.inputmethod.StartInputFlags;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.view.IInputMethodManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.List;
+
+/**
+ * An actual implementation class of {@link IInputMethodManager.Stub} to allow other classes to
+ * focus on handling IPC callbacks.
+ */
+final class IInputMethodManagerImpl extends IInputMethodManager.Stub {
+
+ /**
+ * Tells that the given permission is already verified before the annotated method gets called.
+ */
+ @Retention(SOURCE)
+ @Target({METHOD})
+ @interface PermissionVerified {
+ String value() default "";
+ }
+
+ @BinderThread
+ interface Callback {
+ void addClient(IInputMethodClient client, IRemoteInputConnection inputConnection,
+ int selfReportedDisplayId);
+
+ InputMethodInfo getCurrentInputMethodInfoAsUser(@UserIdInt int userId);
+
+ List<InputMethodInfo> getInputMethodList(@UserIdInt int userId,
+ @DirectBootAwareness int directBootAwareness);
+
+ List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId);
+
+ List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
+ boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId);
+
+ InputMethodSubtype getLastInputMethodSubtype(@UserIdInt int userId);
+
+ boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ @MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason);
+
+ boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason);
+
+ @PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
+ void hideSoftInputFromServerForTest();
+
+ void startInputOrWindowGainedFocusAsync(
+ @StartInputReason int startInputReason, IInputMethodClient client,
+ IBinder windowToken, @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags,
+ @Nullable EditorInfo editorInfo, IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+
+ InputBindResult startInputOrWindowGainedFocus(
+ @StartInputReason int startInputReason, IInputMethodClient client,
+ IBinder windowToken, @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags,
+ @Nullable EditorInfo editorInfo, IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher);
+
+ void showInputMethodPickerFromClient(IInputMethodClient client, int auxiliarySubtypeMode);
+
+ @PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+ void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId);
+
+ @PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
+ boolean isInputMethodPickerShownForTest();
+
+ InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId);
+
+ void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
+ @UserIdInt int userId);
+
+ void setExplicitlyEnabledInputMethodSubtypes(String imeId,
+ @NonNull int[] subtypeHashCodes, @UserIdInt int userId);
+
+ int getInputMethodWindowVisibleHeight(IInputMethodClient client);
+
+ void reportPerceptibleAsync(IBinder windowToken, boolean perceptible);
+
+ @PermissionVerified(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+ void removeImeSurface();
+
+ void removeImeSurfaceFromWindowAsync(IBinder windowToken);
+
+ void startProtoDump(byte[] bytes, int i, String s);
+
+ boolean isImeTraceEnabled();
+
+ @PermissionVerified(Manifest.permission.CONTROL_UI_TRACING)
+ void startImeTrace();
+
+ @PermissionVerified(Manifest.permission.CONTROL_UI_TRACING)
+ void stopImeTrace();
+
+ void startStylusHandwriting(IInputMethodClient client);
+
+ void startConnectionlessStylusHandwriting(IInputMethodClient client, @UserIdInt int userId,
+ @Nullable CursorAnchorInfo cursorAnchorInfo, @Nullable String delegatePackageName,
+ @Nullable String delegatorPackageName,
+ @NonNull IConnectionlessHandwritingCallback callback);
+
+ boolean acceptStylusHandwritingDelegation(@NonNull IInputMethodClient client,
+ @UserIdInt int userId, @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags);
+
+ void acceptStylusHandwritingDelegationAsync(@NonNull IInputMethodClient client,
+ @UserIdInt int userId, @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback);
+
+ void prepareStylusHandwritingDelegation(@NonNull IInputMethodClient client,
+ @UserIdInt int userId, @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName);
+
+ boolean isStylusHandwritingAvailableAsUser(@UserIdInt int userId, boolean connectionless);
+
+ @PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
+ void addVirtualStylusIdForTestSession(IInputMethodClient client);
+
+ @PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
+ void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout);
+
+ IImeTracker getImeTrackerService();
+
+ void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, @NonNull String[] args,
+ @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver,
+ @NonNull Binder self);
+
+ void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args);
+ }
+
+ @NonNull
+ private final Callback mCallback;
+
+ private IInputMethodManagerImpl(@NonNull Callback callback) {
+ mCallback = callback;
+ }
+
+ static IInputMethodManagerImpl create(@NonNull Callback callback) {
+ return new IInputMethodManagerImpl(callback);
+ }
+
+ @Override
+ public void addClient(IInputMethodClient client, IRemoteInputConnection inputmethod,
+ int untrustedDisplayId) {
+ mCallback.addClient(client, inputmethod, untrustedDisplayId);
+ }
+
+ @Override
+ public InputMethodInfo getCurrentInputMethodInfoAsUser(@UserIdInt int userId) {
+ return mCallback.getCurrentInputMethodInfoAsUser(userId);
+ }
+
+ @Override
+ public List<InputMethodInfo> getInputMethodList(@UserIdInt int userId,
+ int directBootAwareness) {
+ return mCallback.getInputMethodList(userId, directBootAwareness);
+ }
+
+ @Override
+ public List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) {
+ return mCallback.getEnabledInputMethodList(userId);
+ }
+
+ @Override
+ public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
+ boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
+ return mCallback.getEnabledInputMethodSubtypeList(imiId, allowsImplicitlyEnabledSubtypes,
+ userId);
+ }
+
+ @Override
+ public InputMethodSubtype getLastInputMethodSubtype(@UserIdInt int userId) {
+ return mCallback.getLastInputMethodSubtype(userId);
+ }
+
+ @Override
+ public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ @MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
+ return mCallback.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
+ resultReceiver, reason);
+ }
+
+ @Override
+ public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ return mCallback.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
+ reason);
+ }
+
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @Override
+ public void hideSoftInputFromServerForTest() {
+ super.hideSoftInputFromServerForTest_enforcePermission();
+
+ mCallback.hideSoftInputFromServerForTest();
+ }
+
+ @Override
+ public InputBindResult startInputOrWindowGainedFocus(
+ @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
+ @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+ int windowFlags, @Nullable EditorInfo editorInfo,
+ IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ return mCallback.startInputOrWindowGainedFocus(
+ startInputReason, client, windowToken, startInputFlags, softInputMode,
+ windowFlags, editorInfo, inputConnection, remoteAccessibilityInputConnection,
+ unverifiedTargetSdkVersion, userId, imeDispatcher);
+ }
+
+ @Override
+ public void startInputOrWindowGainedFocusAsync(@StartInputReason int startInputReason,
+ IInputMethodClient client, IBinder windowToken,
+ @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+ int windowFlags, @Nullable EditorInfo editorInfo,
+ IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ mCallback.startInputOrWindowGainedFocusAsync(
+ startInputReason, client, windowToken, startInputFlags, softInputMode,
+ windowFlags, editorInfo, inputConnection, remoteAccessibilityInputConnection,
+ unverifiedTargetSdkVersion, userId, imeDispatcher, startInputSeq);
+ }
+
+ @Override
+ public void showInputMethodPickerFromClient(IInputMethodClient client,
+ int auxiliarySubtypeMode) {
+ mCallback.showInputMethodPickerFromClient(client, auxiliarySubtypeMode);
+ }
+
+ @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @Override
+ public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
+ super.showInputMethodPickerFromSystem_enforcePermission();
+
+ mCallback.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
+
+ }
+
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @Override
+ public boolean isInputMethodPickerShownForTest() {
+ super.isInputMethodPickerShownForTest_enforcePermission();
+
+ return mCallback.isInputMethodPickerShownForTest();
+ }
+
+ @Override
+ public InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId) {
+ return mCallback.getCurrentInputMethodSubtype(userId);
+ }
+
+ @Override
+ public void setAdditionalInputMethodSubtypes(String id, InputMethodSubtype[] subtypes,
+ @UserIdInt int userId) {
+ mCallback.setAdditionalInputMethodSubtypes(id, subtypes, userId);
+ }
+
+ @Override
+ public void setExplicitlyEnabledInputMethodSubtypes(String imeId, int[] subtypeHashCodes,
+ @UserIdInt int userId) {
+ mCallback.setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes, userId);
+ }
+
+ @Override
+ public int getInputMethodWindowVisibleHeight(IInputMethodClient client) {
+ return mCallback.getInputMethodWindowVisibleHeight(client);
+ }
+
+ @Override
+ public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
+ mCallback.reportPerceptibleAsync(windowToken, perceptible);
+ }
+
+ @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+ @Override
+ public void removeImeSurface() {
+ super.removeImeSurface_enforcePermission();
+
+ mCallback.removeImeSurface();
+ }
+
+ @Override
+ public void removeImeSurfaceFromWindowAsync(IBinder windowToken) {
+ mCallback.removeImeSurfaceFromWindowAsync(windowToken);
+ }
+
+ @Override
+ public void startProtoDump(byte[] protoDump, int source, String where) {
+ mCallback.startProtoDump(protoDump, source, where);
+ }
+
+ @Override
+ public boolean isImeTraceEnabled() {
+ return mCallback.isImeTraceEnabled();
+ }
+
+ @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @Override
+ public void startImeTrace() {
+ super.startImeTrace_enforcePermission();
+
+ mCallback.startImeTrace();
+ }
+
+ @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @Override
+ public void stopImeTrace() {
+ super.stopImeTrace_enforcePermission();
+
+ mCallback.stopImeTrace();
+ }
+
+ @Override
+ public void startStylusHandwriting(IInputMethodClient client) {
+ mCallback.startStylusHandwriting(client);
+ }
+
+ @Override
+ public void startConnectionlessStylusHandwriting(IInputMethodClient client,
+ @UserIdInt int userId, CursorAnchorInfo cursorAnchorInfo,
+ String delegatePackageName, String delegatorPackageName,
+ IConnectionlessHandwritingCallback callback) {
+ mCallback.startConnectionlessStylusHandwriting(client, userId, cursorAnchorInfo,
+ delegatePackageName, delegatorPackageName, callback);
+ }
+
+ @Override
+ public void prepareStylusHandwritingDelegation(IInputMethodClient client, @UserIdInt int userId,
+ String delegatePackageName, String delegatorPackageName) {
+ mCallback.prepareStylusHandwritingDelegation(client, userId,
+ delegatePackageName, delegatorPackageName);
+ }
+
+ @Override
+ public boolean acceptStylusHandwritingDelegation(IInputMethodClient client,
+ @UserIdInt int userId, String delegatePackageName, String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags) {
+ return mCallback.acceptStylusHandwritingDelegation(client, userId,
+ delegatePackageName, delegatorPackageName, flags);
+ }
+
+ @Override
+ public void acceptStylusHandwritingDelegationAsync(IInputMethodClient client,
+ @UserIdInt int userId, String delegatePackageName, String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags,
+ IBooleanListener callback) {
+ mCallback.acceptStylusHandwritingDelegationAsync(client, userId,
+ delegatePackageName, delegatorPackageName, flags, callback);
+ }
+
+ @Override
+ public boolean isStylusHandwritingAvailableAsUser(@UserIdInt int userId,
+ boolean connectionless) {
+ return mCallback.isStylusHandwritingAvailableAsUser(userId, connectionless);
+ }
+
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @Override
+ public void addVirtualStylusIdForTestSession(IInputMethodClient client) {
+ super.addVirtualStylusIdForTestSession_enforcePermission();
+
+ mCallback.addVirtualStylusIdForTestSession(client);
+ }
+
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @Override
+ public void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout) {
+ super.setStylusWindowIdleTimeoutForTest_enforcePermission();
+
+ mCallback.setStylusWindowIdleTimeoutForTest(client, timeout);
+ }
+
+ @Override
+ public IImeTracker getImeTrackerService() {
+ return mCallback.getImeTrackerService();
+ }
+
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) {
+ mCallback.onShellCommand(in, out, err, args, callback, resultReceiver, this);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mCallback.dump(fd, pw, args);
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index a100fe0..3e23f97 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -16,10 +16,12 @@
package com.android.server.inputmethod;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -410,7 +412,7 @@
Slog.v(TAG,
"Removing window token: " + mCurToken + " for display: " + curTokenDisplayId);
}
- mWindowManagerInternal.removeWindowToken(mCurToken, false /* removeWindows */,
+ mWindowManagerInternal.removeWindowToken(mCurToken, true /* removeWindows */,
false /* animateExit */, curTokenDisplayId);
mCurToken = null;
}
@@ -452,9 +454,12 @@
intent.setComponent(component);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.input_method_binding_label);
+ var options = ActivityOptions.makeBasic()
+ .setPendingIntentCreatorBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_DENIED);
intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
- PendingIntent.FLAG_IMMUTABLE));
+ PendingIntent.FLAG_IMMUTABLE, options.toBundle()));
return intent;
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index c6a48ec..2583d73 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -61,7 +61,6 @@
import android.annotation.BinderThread;
import android.annotation.DrawableRes;
import android.annotation.DurationMillisLong;
-import android.annotation.EnforcePermission;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -172,7 +171,6 @@
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
-import com.android.internal.view.IInputMethodManager;
import com.android.server.AccessibilityManagerInternal;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
@@ -211,8 +209,9 @@
/**
* This class provides a system service that manages input methods.
*/
-public final class InputMethodManagerService extends IInputMethodManager.Stub
- implements Handler.Callback {
+public final class InputMethodManagerService implements IInputMethodManagerImpl.Callback,
+ ZeroJankProxy.Callback, Handler.Callback {
+
// Virtual device id for test.
private static final Integer VIRTUAL_STYLUS_ID_FOR_TEST = 999999;
static final boolean DEBUG = false;
@@ -284,9 +283,12 @@
final Context mContext;
final Resources mRes;
private final Handler mHandler;
- @NonNull
+
@MultiUserUnawareField
- private InputMethodSettings mSettings;
+ @UserIdInt
+ @GuardedBy("ImfLock.class")
+ private int mCurrentUserId;
+
@MultiUserUnawareField
final SettingsObserver mSettingsObserver;
final WindowManagerInternal mWindowManagerInternal;
@@ -491,7 +493,7 @@
@GuardedBy("ImfLock.class")
@Nullable
InputMethodInfo queryInputMethodForCurrentUserLocked(@NonNull String imeId) {
- return mSettings.getMethodMap().get(imeId);
+ return InputMethodSettingsRepository.get(mCurrentUserId).getMethodMap().get(imeId);
}
/**
@@ -812,7 +814,8 @@
InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches();
} else {
boolean enabledChanged = false;
- String newEnabled = mSettings.getEnabledInputMethodsStr();
+ String newEnabled = InputMethodSettingsRepository.get(mCurrentUserId)
+ .getEnabledInputMethodsStr();
if (!mLastEnabled.equals(newEnabled)) {
mLastEnabled = newEnabled;
enabledChanged = true;
@@ -844,9 +847,11 @@
// sender userId can be a real user ID or USER_ALL.
final int senderUserId = pendingResult.getSendingUserId();
if (senderUserId != UserHandle.USER_ALL) {
- if (senderUserId != mSettings.getUserId()) {
- // A background user is trying to hide the dialog. Ignore.
- return;
+ synchronized (ImfLock.class) {
+ if (senderUserId != mCurrentUserId) {
+ // A background user is trying to hide the dialog. Ignore.
+ return;
+ }
}
}
mMenuController.hideInputMethodMenu();
@@ -870,9 +875,14 @@
if (!mSystemReady) {
return;
}
- mSettings = queryInputMethodServicesInternal(mContext, mSettings.getUserId(),
- AdditionalSubtypeMapRepository.get(mSettings.getUserId()),
- DirectBootAwareness.AUTO);
+ for (int userId : mUserManagerInternal.getUserIds()) {
+ final InputMethodSettings settings = queryInputMethodServicesInternal(
+ mContext,
+ userId,
+ AdditionalSubtypeMapRepository.get(userId),
+ DirectBootAwareness.AUTO);
+ InputMethodSettingsRepository.put(userId, settings);
+ }
postInputMethodSettingUpdatedLocked(true /* resetDefaultEnabledIme */);
// If the locale is changed, needs to reset the default ime
resetDefaultImeLocked(mContext);
@@ -933,7 +943,7 @@
@GuardedBy("ImfLock.class")
private boolean isChangingPackagesOfCurrentUserLocked() {
final int userId = getChangingUserId();
- final boolean retval = userId == mSettings.getUserId();
+ final boolean retval = userId == mCurrentUserId;
if (DEBUG) {
if (!retval) {
Slog.d(TAG, "--- ignore this call back from a background user: " + userId);
@@ -948,8 +958,10 @@
if (!isChangingPackagesOfCurrentUserLocked()) {
return false;
}
- String curInputMethodId = mSettings.getSelectedInputMethod();
- final List<InputMethodInfo> methodList = mSettings.getMethodList();
+ final InputMethodSettings settings =
+ InputMethodSettingsRepository.get(mCurrentUserId);
+ String curInputMethodId = settings.getSelectedInputMethod();
+ final List<InputMethodInfo> methodList = settings.getMethodList();
final int numImes = methodList.size();
if (curInputMethodId != null) {
for (int i = 0; i < numImes; i++) {
@@ -1066,16 +1078,10 @@
private void onFinishPackageChangesInternal() {
synchronized (ImfLock.class) {
final int userId = getChangingUserId();
- final boolean isCurrentUser = (userId == mSettings.getUserId());
+ final boolean isCurrentUser = (userId == mCurrentUserId);
final AdditionalSubtypeMap additionalSubtypeMap =
AdditionalSubtypeMapRepository.get(userId);
- final InputMethodSettings settings;
- if (isCurrentUser) {
- settings = mSettings;
- } else {
- settings = queryInputMethodServicesInternal(mContext, userId,
- additionalSubtypeMap, DirectBootAwareness.AUTO);
- }
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
InputMethodInfo curIm = null;
String curInputMethodId = settings.getSelectedInputMethod();
@@ -1119,16 +1125,17 @@
AdditionalSubtypeMapRepository.putAndSave(userId, newAdditionalSubtypeMap,
settings.getMethodMap());
}
+ if (isCurrentUser
+ && !(additionalSubtypeChanged || shouldRebuildInputMethodListLocked())) {
+ return;
+ }
+ final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
+ userId, newAdditionalSubtypeMap, DirectBootAwareness.AUTO);
+ InputMethodSettingsRepository.put(userId, newSettings);
if (!isCurrentUser) {
return;
}
-
- if (!(additionalSubtypeChanged || shouldRebuildInputMethodListLocked())) {
- return;
- }
- mSettings = queryInputMethodServicesInternal(mContext, userId,
- newAdditionalSubtypeMap, DirectBootAwareness.AUTO);
postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */);
boolean changed = false;
@@ -1236,21 +1243,14 @@
@Override
public void onStart() {
mService.publishLocalService();
- IInputMethodManager.Stub service;
+ IInputMethodManagerImpl.Callback service;
if (Flags.useZeroJankProxy()) {
- service =
- new ZeroJankProxy(
- mService.mHandler::post,
- mService,
- () -> {
- synchronized (ImfLock.class) {
- return mService.isInputShown();
- }
- });
+ service = new ZeroJankProxy(mService.mHandler::post, mService);
} else {
service = mService;
}
- publishBinderService(Context.INPUT_METHOD_SERVICE, service, false /*allowIsolated*/,
+ publishBinderService(Context.INPUT_METHOD_SERVICE,
+ IInputMethodManagerImpl.create(service), false /*allowIsolated*/,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
}
@@ -1289,21 +1289,20 @@
void onUnlockUser(@UserIdInt int userId) {
synchronized (ImfLock.class) {
- final int currentUserId = mSettings.getUserId();
if (DEBUG) {
- Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + currentUserId);
- }
- if (userId != currentUserId) {
- return;
+ Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + mCurrentUserId);
}
if (!mSystemReady) {
return;
}
- mSettings = queryInputMethodServicesInternal(mContext, userId,
- AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO);
- // We need to rebuild IMEs.
- postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */);
- updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
+ final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
+ userId, AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO);
+ InputMethodSettingsRepository.put(userId, newSettings);
+ if (mCurrentUserId == userId) {
+ // We need to rebuild IMEs.
+ postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */);
+ updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
+ }
}
}
@@ -1336,83 +1335,85 @@
Context context,
@Nullable ServiceThread serviceThreadForTesting,
@Nullable InputMethodBindingController bindingControllerForTesting) {
- mContext = context;
- mRes = context.getResources();
- SecureSettingsWrapper.onStart(mContext);
- // TODO(b/196206770): Disallow I/O on this thread. Currently it's needed for loading
- // additional subtypes in switchUserOnHandlerLocked().
- final ServiceThread thread =
- serviceThreadForTesting != null
- ? serviceThreadForTesting
- : new ServiceThread(
- HANDLER_THREAD_NAME,
- Process.THREAD_PRIORITY_FOREGROUND,
- true /* allowIo */);
- thread.start();
- mHandler = Handler.createAsync(thread.getLooper(), this);
- SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mHandler);
- mImeTrackerService = new ImeTrackerService(serviceThreadForTesting != null
- ? serviceThreadForTesting.getLooper() : Looper.getMainLooper());
- // Note: SettingsObserver doesn't register observers in its constructor.
- mSettingsObserver = new SettingsObserver(mHandler);
- mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
- mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
- mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
- mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
- mImePlatformCompatUtils = new ImePlatformCompatUtils();
- mInputMethodDeviceConfigs = new InputMethodDeviceConfigs();
- mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
-
- mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
-
- mShowOngoingImeSwitcherForPhones = false;
-
- AdditionalSubtypeMapRepository.initialize(mHandler);
-
- final int userId = mActivityManagerInternal.getCurrentUserId();
-
- // mSettings should be created before buildInputMethodListLocked
- mSettings = InputMethodSettings.createEmptyMap(userId);
-
- mSwitchingController =
- InputMethodSubtypeSwitchingController.createInstanceLocked(context,
- mSettings.getMethodMap(), userId);
- mHardwareKeyboardShortcutController =
- new HardwareKeyboardShortcutController(mSettings.getMethodMap(),
- mSettings.getUserId());
- mMenuController = new InputMethodMenuController(this);
- mBindingController =
- bindingControllerForTesting != null
- ? bindingControllerForTesting
- : new InputMethodBindingController(this);
- mAutofillController = new AutofillSuggestionsController(this);
-
- mVisibilityStateComputer = new ImeVisibilityStateComputer(this);
- mVisibilityApplier = new DefaultImeVisibilityApplier(this);
-
- mClientController = new ClientController(mPackageManagerInternal);
synchronized (ImfLock.class) {
+ mContext = context;
+ mRes = context.getResources();
+ SecureSettingsWrapper.onStart(mContext);
+
+ // TODO(b/196206770): Disallow I/O on this thread. Currently it's needed for loading
+ // additional subtypes in switchUserOnHandlerLocked().
+ final ServiceThread thread =
+ serviceThreadForTesting != null
+ ? serviceThreadForTesting
+ : new ServiceThread(
+ HANDLER_THREAD_NAME,
+ Process.THREAD_PRIORITY_FOREGROUND,
+ true /* allowIo */);
+ thread.start();
+ mHandler = Handler.createAsync(thread.getLooper(), this);
+ SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mHandler);
+ mImeTrackerService = new ImeTrackerService(serviceThreadForTesting != null
+ ? serviceThreadForTesting.getLooper() : Looper.getMainLooper());
+ // Note: SettingsObserver doesn't register observers in its constructor.
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
+ mImePlatformCompatUtils = new ImePlatformCompatUtils();
+ mInputMethodDeviceConfigs = new InputMethodDeviceConfigs();
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+
+ mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
+
+ mShowOngoingImeSwitcherForPhones = false;
+
+ // InputMethodSettingsRepository should be initialized before buildInputMethodListLocked
+ InputMethodSettingsRepository.initialize(mHandler, mContext);
+ AdditionalSubtypeMapRepository.initialize(mHandler, mContext);
+
+ mCurrentUserId = mActivityManagerInternal.getCurrentUserId();
+
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+
+ mSwitchingController =
+ InputMethodSubtypeSwitchingController.createInstanceLocked(context,
+ settings.getMethodMap(), settings.getUserId());
+ mHardwareKeyboardShortcutController =
+ new HardwareKeyboardShortcutController(settings.getMethodMap(),
+ settings.getUserId());
+ mMenuController = new InputMethodMenuController(this);
+ mBindingController =
+ bindingControllerForTesting != null
+ ? bindingControllerForTesting
+ : new InputMethodBindingController(this);
+ mAutofillController = new AutofillSuggestionsController(this);
+
+ mVisibilityStateComputer = new ImeVisibilityStateComputer(this);
+ mVisibilityApplier = new DefaultImeVisibilityApplier(this);
+
+ mClientController = new ClientController(mPackageManagerInternal);
mClientController.addClientControllerCallback(c -> onClientRemoved(c));
mImeBindingState = ImeBindingState.newEmptyState();
- }
- mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
- com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
- mNonPreemptibleInputMethods = mRes.getStringArray(
- com.android.internal.R.array.config_nonPreemptibleInputMethods);
- IntConsumer toolTypeConsumer =
- Flags.useHandwritingListenerForTooltype()
- ? toolType -> onUpdateEditorToolType(toolType) : null;
- Runnable discardDelegationTextRunnable = () -> discardHandwritingDelegationText();
- mHwController = new HandwritingModeController(mContext, thread.getLooper(),
- new InkWindowInitializer(), toolTypeConsumer, discardDelegationTextRunnable);
- registerDeviceListenerAndCheckStylusSupport();
+ mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
+ com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
+ mNonPreemptibleInputMethods = mRes.getStringArray(
+ com.android.internal.R.array.config_nonPreemptibleInputMethods);
+ IntConsumer toolTypeConsumer =
+ Flags.useHandwritingListenerForTooltype()
+ ? toolType -> onUpdateEditorToolType(toolType) : null;
+ Runnable discardDelegationTextRunnable = () -> discardHandwritingDelegationText();
+ mHwController = new HandwritingModeController(mContext, thread.getLooper(),
+ new InkWindowInitializer(), toolTypeConsumer, discardDelegationTextRunnable);
+ registerDeviceListenerAndCheckStylusSupport();
+ }
}
@GuardedBy("ImfLock.class")
@UserIdInt
int getCurrentImeUserIdLocked() {
- return mSettings.getUserId();
+ return mCurrentUserId;
}
private final class InkWindowInitializer implements Runnable {
@@ -1448,12 +1449,13 @@
private void resetDefaultImeLocked(Context context) {
// Do not reset the default (current) IME when it is a 3rd-party IME
String selectedMethodId = getSelectedMethodIdLocked();
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
if (selectedMethodId != null
- && !mSettings.getMethodMap().get(selectedMethodId).isSystem()) {
+ && !settings.getMethodMap().get(selectedMethodId).isSystem()) {
return;
}
final List<InputMethodInfo> suitableImes = InputMethodInfoUtils.getDefaultEnabledImes(
- context, mSettings.getEnabledInputMethodList());
+ context, settings.getEnabledInputMethodList());
if (suitableImes.isEmpty()) {
Slog.i(TAG, "No default found");
return;
@@ -1509,7 +1511,7 @@
IInputMethodClientInvoker clientToBeReset) {
if (DEBUG) {
Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
- + " currentUserId=" + mSettings.getUserId());
+ + " currentUserId=" + mCurrentUserId);
}
maybeInitImeNavbarConfigLocked(newUserId);
@@ -1517,8 +1519,9 @@
// ContentObserver should be registered again when the user is changed
mSettingsObserver.registerContentObserverLocked(newUserId);
- mSettings = InputMethodSettings.createEmptyMap(newUserId);
- final String defaultImiId = mSettings.getSelectedInputMethod();
+ mCurrentUserId = newUserId;
+ final String defaultImiId = SecureSettingsWrapper.getString(
+ Settings.Secure.DEFAULT_INPUT_METHOD, null, newUserId);
if (DEBUG) {
Slog.d(TAG, "Switching user stage 2/3. newUserId=" + newUserId
@@ -1536,10 +1539,9 @@
// and user switch would not happen at that time.
resetCurrentMethodAndClientLocked(UnbindReason.SWITCH_USER);
- mSettings = queryInputMethodServicesInternal(mContext, newUserId,
- AdditionalSubtypeMapRepository.get(newUserId), DirectBootAwareness.AUTO);
+ final InputMethodSettings newSettings = InputMethodSettingsRepository.get(newUserId);
postInputMethodSettingUpdatedLocked(initialUserSwitch /* resetDefaultEnabledIme */);
- if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
+ if (TextUtils.isEmpty(newSettings.getSelectedInputMethod())) {
// This is the first time of the user switch and
// set the current ime to the proper one.
resetDefaultImeLocked(mContext);
@@ -1549,12 +1551,12 @@
if (initialUserSwitch) {
InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
getPackageManagerForUser(mContext, newUserId),
- mSettings.getEnabledInputMethodList());
+ newSettings.getEnabledInputMethodList());
}
if (DEBUG) {
Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId
- + " selectedIme=" + mSettings.getSelectedInputMethod());
+ + " selectedIme=" + newSettings.getSelectedInputMethod());
}
if (mIsInteractive && clientToBeReset != null) {
@@ -1577,7 +1579,7 @@
}
if (!mSystemReady) {
mSystemReady = true;
- final int currentUserId = mSettings.getUserId();
+ final int currentUserId = mCurrentUserId;
mStatusBarManagerInternal =
LocalServices.getService(StatusBarManagerInternal.class);
hideStatusBarIconLocked();
@@ -1598,7 +1600,7 @@
// the "mImeDrawsImeNavBarResLazyInitFuture" field.
synchronized (ImfLock.class) {
mImeDrawsImeNavBarResLazyInitFuture = null;
- if (currentUserId != mSettings.getUserId()) {
+ if (currentUserId != mCurrentUserId) {
// This means that the current user is already switched to other user
// before the background task is executed. In this scenario the relevant
// field should already be initialized.
@@ -1617,17 +1619,19 @@
UserHandle.ALL, broadcastFilterForAllUsers, null, null,
Context.RECEIVER_EXPORTED);
- final String defaultImiId = mSettings.getSelectedInputMethod();
+ final String defaultImiId = SecureSettingsWrapper.getString(
+ Settings.Secure.DEFAULT_INPUT_METHOD, null, currentUserId);
final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
- mSettings = queryInputMethodServicesInternal(mContext, currentUserId,
- AdditionalSubtypeMapRepository.get(mSettings.getUserId()),
+ final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
+ currentUserId, AdditionalSubtypeMapRepository.get(currentUserId),
DirectBootAwareness.AUTO);
+ InputMethodSettingsRepository.put(currentUserId, newSettings);
postInputMethodSettingUpdatedLocked(
!imeSelectedOnBoot /* resetDefaultEnabledIme */);
updateFromSettingsLocked(true);
InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
getPackageManagerForUser(mContext, currentUserId),
- mSettings.getEnabledInputMethodList());
+ newSettings.getEnabledInputMethodList());
}
}
}
@@ -1674,7 +1678,7 @@
}
synchronized (ImfLock.class) {
final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
- mSettings.getUserId(), null);
+ mCurrentUserId, null);
if (resolvedUserIds.length != 1) {
return Collections.emptyList();
}
@@ -1697,7 +1701,7 @@
}
synchronized (ImfLock.class) {
final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
- mSettings.getUserId(), null);
+ mCurrentUserId, null);
if (resolvedUserIds.length != 1) {
return Collections.emptyList();
}
@@ -1725,14 +1729,12 @@
}
// Check if selected IME of current user supports handwriting.
- if (userId == mSettings.getUserId()) {
+ if (userId == mCurrentUserId) {
return mBindingController.supportsStylusHandwriting()
&& (!connectionless
|| mBindingController.supportsConnectionlessStylusHandwriting());
}
- //TODO(b/197848765): This can be optimized by caching multi-user methodMaps/methodList.
- //TODO(b/210039666): use cache.
- final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
final InputMethodInfo imi = settings.getMethodMap().get(
settings.getSelectedInputMethod());
return imi != null && imi.supportsStylusHandwriting()
@@ -1756,9 +1758,8 @@
private List<InputMethodInfo> getInputMethodListLocked(@UserIdInt int userId,
@DirectBootAwareness int directBootAwareness, int callingUid) {
final InputMethodSettings settings;
- if (userId == mSettings.getUserId()
- && directBootAwareness == DirectBootAwareness.AUTO) {
- settings = mSettings;
+ if (directBootAwareness == DirectBootAwareness.AUTO) {
+ settings = InputMethodSettingsRepository.get(userId);
} else {
final AdditionalSubtypeMap additionalSubtypeMap =
AdditionalSubtypeMapRepository.get(userId);
@@ -1776,15 +1777,8 @@
@GuardedBy("ImfLock.class")
private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId,
int callingUid) {
- final ArrayList<InputMethodInfo> methodList;
- final InputMethodSettings settings;
- if (userId == mSettings.getUserId()) {
- methodList = mSettings.getEnabledInputMethodList();
- settings = mSettings;
- } else {
- settings = queryMethodMapForUserLocked(userId);
- methodList = settings.getEnabledInputMethodList();
- }
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
+ final ArrayList<InputMethodInfo> methodList = settings.getEnabledInputMethodList();
// filter caller's access to input methods
methodList.removeIf(imi ->
!canCallerAccessInputMethod(imi.getPackageName(), callingUid, userId, settings));
@@ -1842,22 +1836,7 @@
@GuardedBy("ImfLock.class")
private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(String imiId,
boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId, int callingUid) {
- if (userId == mSettings.getUserId()) {
- final InputMethodInfo imi;
- String selectedMethodId = getSelectedMethodIdLocked();
- if (imiId == null && selectedMethodId != null) {
- imi = mSettings.getMethodMap().get(selectedMethodId);
- } else {
- imi = mSettings.getMethodMap().get(imiId);
- }
- if (imi == null || !canCallerAccessInputMethod(
- imi.getPackageName(), callingUid, userId, mSettings)) {
- return Collections.emptyList();
- }
- return mSettings.getEnabledInputMethodSubtypeList(
- imi, allowsImplicitlyEnabledSubtypes);
- }
- final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
final InputMethodInfo imi = settings.getMethodMap().get(imiId);
if (imi == null) {
return Collections.emptyList();
@@ -1934,10 +1913,10 @@
}
@Nullable
- ClientState getClientState(IInputMethodClient client) {
- synchronized (ImfLock.class) {
- return mClientController.getClient(client.asBinder());
- }
+ @GuardedBy("ImfLock.class")
+ @Override
+ public ClientState getClientStateLocked(IInputMethodClient client) {
+ return mClientController.getClient(client.asBinder());
}
// TODO(b/314150112): Move this to ClientController.
@@ -2016,7 +1995,8 @@
}
@GuardedBy("ImfLock.class")
- private boolean isInputShown() {
+ @Override
+ public boolean isInputShownLocked() {
return mVisibilityStateComputer.isInputShown();
}
@@ -2037,7 +2017,7 @@
final boolean restarting = !initial;
final Binder startInputToken = new Binder();
- final StartInputInfo info = new StartInputInfo(mSettings.getUserId(),
+ final StartInputInfo info = new StartInputInfo(mCurrentUserId,
getCurTokenLocked(),
mCurTokenDisplayId, getCurIdLocked(), startInputReason, restarting,
UserHandle.getUserId(mCurClient.mUid),
@@ -2051,9 +2031,9 @@
// same-user scenarios.
// That said ignoring cross-user scenario will never affect IMEs that do not have
// INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
- if (mSettings.getUserId() == UserHandle.getUserId(
+ if (mCurrentUserId == UserHandle.getUserId(
mCurClient.mUid)) {
- mPackageManagerInternal.grantImplicitAccess(mSettings.getUserId(),
+ mPackageManagerInternal.grantImplicitAccess(mCurrentUserId,
null /* intent */, UserHandle.getAppId(getCurMethodUidLocked()),
mCurClient.mUid, true /* direct */);
}
@@ -2076,7 +2056,8 @@
}
String curId = getCurIdLocked();
- final InputMethodInfo curInputMethodInfo = mSettings.getMethodMap().get(curId);
+ final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(mCurrentUserId)
+ .getMethodMap().get(curId);
final boolean suppressesSpellChecker =
curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
final SparseArray<IAccessibilityInputMethodSession> accessibilityInputMethodSessions =
@@ -2264,17 +2245,18 @@
return currentMethodId;
}
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
final int oldDeviceId = mDeviceIdToShowIme;
mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme);
if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
if (oldDeviceId == DEVICE_ID_DEFAULT) {
return currentMethodId;
}
- final String defaultDeviceMethodId = mSettings.getSelectedDefaultDeviceInputMethod();
+ final String defaultDeviceMethodId = settings.getSelectedDefaultDeviceInputMethod();
if (DEBUG) {
Slog.v(TAG, "Restoring default device input method: " + defaultDeviceMethodId);
}
- mSettings.putSelectedDefaultDeviceInputMethod(null);
+ settings.putSelectedDefaultDeviceInputMethod(null);
return defaultDeviceMethodId;
}
@@ -2282,7 +2264,7 @@
mVirtualDeviceMethodMap.get(mDeviceIdToShowIme, currentMethodId);
if (Objects.equals(deviceMethodId, currentMethodId)) {
return currentMethodId;
- } else if (!mSettings.getMethodMap().containsKey(deviceMethodId)) {
+ } else if (!settings.getMethodMap().containsKey(deviceMethodId)) {
if (DEBUG) {
Slog.v(TAG, "Disabling IME on virtual device with id " + mDeviceIdToShowIme
+ " because its custom input method is not available: " + deviceMethodId);
@@ -2294,7 +2276,7 @@
if (DEBUG) {
Slog.v(TAG, "Storing default device input method " + currentMethodId);
}
- mSettings.putSelectedDefaultDeviceInputMethod(currentMethodId);
+ settings.putSelectedDefaultDeviceInputMethod(currentMethodId);
}
if (DEBUG) {
Slog.v(TAG, "Switching current input method from " + currentMethodId
@@ -2324,7 +2306,8 @@
if (isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags)) {
return false;
}
- final InputMethodInfo imi = mSettings.getMethodMap().get(selectedMethodId);
+ final InputMethodInfo imi = InputMethodSettingsRepository.get(mCurrentUserId)
+ .getMethodMap().get(selectedMethodId);
if (imi == null) {
return false;
}
@@ -2668,7 +2651,7 @@
} else if (packageName != null) {
if (DEBUG) Slog.d(TAG, "show a small icon for the input method");
final PackageManager userAwarePackageManager =
- getPackageManagerForUser(mContext, mSettings.getUserId());
+ getPackageManagerForUser(mContext, mCurrentUserId);
ApplicationInfo applicationInfo = null;
try {
applicationInfo = userAwarePackageManager.getApplicationInfo(packageName,
@@ -2730,7 +2713,7 @@
return false;
}
if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded()
- && mWindowManagerInternal.isKeyguardSecure(mSettings.getUserId())) {
+ && mWindowManagerInternal.isKeyguardSecure(mCurrentUserId)) {
return false;
}
if ((visibility & InputMethodService.IME_ACTIVE) == 0
@@ -2747,7 +2730,8 @@
return false;
}
- List<InputMethodInfo> imes = mSettings.getEnabledInputMethodListWithFilter(
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ List<InputMethodInfo> imes = settings.getEnabledInputMethodListWithFilter(
InputMethodInfo::shouldShowInInputMethodPicker);
final int numImes = imes.size();
if (numImes > 2) return true;
@@ -2759,7 +2743,7 @@
for (int i = 0; i < numImes; ++i) {
final InputMethodInfo imi = imes.get(i);
final List<InputMethodSubtype> subtypes =
- mSettings.getEnabledInputMethodSubtypeList(imi, true);
+ settings.getEnabledInputMethodSubtypeList(imi, true);
final int subtypeCount = subtypes.size();
if (subtypeCount == 0) {
++nonAuxCount;
@@ -2911,11 +2895,12 @@
@GuardedBy("ImfLock.class")
void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
if (enabledMayChange) {
final PackageManager userAwarePackageManager = getPackageManagerForUser(mContext,
- mSettings.getUserId());
+ settings.getUserId());
- List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodList();
+ List<InputMethodInfo> enabled = settings.getEnabledInputMethodList();
for (int i = 0; i < enabled.size(); i++) {
// We allow the user to select "disabled until used" apps, so if they
// are enabling one of those here we now need to make it enabled.
@@ -2942,20 +2927,20 @@
if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
String ime = SecureSettingsWrapper.getString(
- Settings.Secure.DEFAULT_INPUT_METHOD, null, mSettings.getUserId());
+ Settings.Secure.DEFAULT_INPUT_METHOD, null, settings.getUserId());
String defaultDeviceIme = SecureSettingsWrapper.getString(
- Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, mSettings.getUserId());
+ Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId());
if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
if (DEBUG) {
Slog.v(TAG, "Current input method " + ime + " differs from the stored default"
- + " device input method for user " + mSettings.getUserId()
+ + " device input method for user " + settings.getUserId()
+ " - restoring " + defaultDeviceIme);
}
SecureSettingsWrapper.putString(
Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme,
- mSettings.getUserId());
+ settings.getUserId());
SecureSettingsWrapper.putString(
- Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, mSettings.getUserId());
+ Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId());
}
}
@@ -2963,14 +2948,14 @@
// ENABLED_INPUT_METHODS is taking care of keeping them correctly in
// sync, so we will never have a DEFAULT_INPUT_METHOD that is not
// enabled.
- String id = mSettings.getSelectedInputMethod();
+ String id = settings.getSelectedInputMethod();
// There is no input method selected, try to choose new applicable input method.
if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) {
- id = mSettings.getSelectedInputMethod();
+ id = settings.getSelectedInputMethod();
}
if (!TextUtils.isEmpty(id)) {
try {
- setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
+ setInputMethodLocked(id, settings.getSelectedInputMethodSubtypeId(id));
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Unknown input method from prefs: " + id, e);
resetCurrentMethodAndClientLocked(UnbindReason.SWITCH_IME_FAILED);
@@ -2981,18 +2966,18 @@
}
// TODO: Instantiate mSwitchingController for each user.
- if (mSettings.getUserId() == mSwitchingController.getUserId()) {
- mSwitchingController.resetCircularListLocked(mSettings.getMethodMap());
+ if (settings.getUserId() == mSwitchingController.getUserId()) {
+ mSwitchingController.resetCircularListLocked(settings.getMethodMap());
} else {
mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
- mContext, mSettings.getMethodMap(), mSettings.getUserId());
+ mContext, settings.getMethodMap(), settings.getUserId());
}
// TODO: Instantiate mHardwareKeyboardShortcutController for each user.
- if (mSettings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
- mHardwareKeyboardShortcutController.reset(mSettings.getMethodMap());
+ if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+ mHardwareKeyboardShortcutController.reset(settings.getMethodMap());
} else {
mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
- mSettings.getMethodMap(), mSettings.getUserId());
+ settings.getMethodMap(), settings.getUserId());
}
sendOnNavButtonFlagsChangedLocked();
}
@@ -3016,14 +3001,15 @@
@GuardedBy("ImfLock.class")
void setInputMethodLocked(String id, int subtypeId, int deviceId) {
- InputMethodInfo info = mSettings.getMethodMap().get(id);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ InputMethodInfo info = settings.getMethodMap().get(id);
if (info == null) {
throw getExceptionForUnknownImeId(id);
}
// See if we need to notify a subtype change within the same IME.
if (id.equals(getSelectedMethodIdLocked())) {
- final int userId = mSettings.getUserId();
+ final int userId = settings.getUserId();
final int subtypeCount = info.getSubtypeCount();
if (subtypeCount <= 0) {
notifyInputMethodSubtypeChangedLocked(userId, info, null);
@@ -3064,7 +3050,7 @@
// method is a custom one specific to a virtual device. So only update the settings
// entry used to restore the default device input method once we want to show the IME
// back on the default device.
- mSettings.putSelectedDefaultDeviceInputMethod(id);
+ settings.putSelectedDefaultDeviceInputMethod(id);
return;
}
IInputMethodInvoker curMethod = getCurMethodLocked();
@@ -3132,11 +3118,16 @@
public void startConnectionlessStylusHandwriting(IInputMethodClient client, int userId,
@Nullable CursorAnchorInfo cursorAnchorInfo, @Nullable String delegatePackageName,
@Nullable String delegatorPackageName,
- @NonNull IConnectionlessHandwritingCallback callback) throws RemoteException {
+ @NonNull IConnectionlessHandwritingCallback callback) {
synchronized (ImfLock.class) {
if (!mBindingController.supportsConnectionlessStylusHandwriting()) {
Slog.w(TAG, "Connectionless stylus handwriting mode unsupported by IME.");
- callback.onError(CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED);
+ try {
+ callback.onError(CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED", e);
+ e.rethrowAsRuntimeException();
+ }
return;
}
}
@@ -3147,7 +3138,12 @@
synchronized (ImfLock.class) {
if (!mClientController.verifyClientAndPackageMatch(client, delegatorPackageName)) {
Slog.w(TAG, "startConnectionlessStylusHandwriting() fail");
- callback.onError(CONNECTIONLESS_HANDWRITING_ERROR_OTHER);
+ try {
+ callback.onError(CONNECTIONLESS_HANDWRITING_ERROR_OTHER);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report CONNECTIONLESS_HANDWRITING_ERROR_OTHER", e);
+ e.rethrowAsRuntimeException();
+ }
throw new IllegalArgumentException("Delegator doesn't match UID");
}
}
@@ -3171,7 +3167,12 @@
if (!startStylusHandwriting(
client, false, immsCallback, cursorAnchorInfo, isForDelegation)) {
- callback.onError(CONNECTIONLESS_HANDWRITING_ERROR_OTHER);
+ try {
+ callback.onError(CONNECTIONLESS_HANDWRITING_ERROR_OTHER);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report CONNECTIONLESS_HANDWRITING_ERROR_OTHER", e);
+ e.rethrowAsRuntimeException();
+ }
}
}
@@ -3271,11 +3272,15 @@
@UserIdInt int userId,
@NonNull String delegatePackageName,
@NonNull String delegatorPackageName,
- @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback)
- throws RemoteException {
+ @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback) {
boolean result = acceptStylusHandwritingDelegation(
client, userId, delegatePackageName, delegatorPackageName, flags);
- callback.onResult(result);
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report result=" + result, e);
+ e.rethrowAsRuntimeException();
+ }
}
@Override
@@ -3366,7 +3371,7 @@
@GuardedBy("ImfLock.class")
boolean showCurrentInputLocked(IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
- int lastClickToolType, @Nullable ResultReceiver resultReceiver,
+ @MotionEvent.ToolType int lastClickToolType, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
if (!mVisibilityStateComputer.onImeShowFlags(statsToken, flags)) {
return false;
@@ -3412,7 +3417,7 @@
"InputMethodManagerService#hideSoftInput");
synchronized (ImfLock.class) {
if (!canInteractWithImeLocked(uid, client, "hideSoftInput", statsToken)) {
- if (isInputShown()) {
+ if (isInputShownLocked()) {
ImeTracker.forLogging().onFailed(
statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
} else {
@@ -3435,10 +3440,8 @@
}
@Override
- @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
public void hideSoftInputFromServerForTest() {
- super.hideSoftInputFromServerForTest_enforcePermission();
-
synchronized (ImfLock.class) {
hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_SOFT_INPUT);
@@ -3471,7 +3474,7 @@
// TODO(b/246309664): Clean up IMMS#mImeWindowVis
IInputMethodInvoker curMethod = getCurMethodLocked();
final boolean shouldHideSoftInput = curMethod != null
- && (isInputShown() || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
+ && (isInputShownLocked() || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
mVisibilityStateComputer.requestImeVisibility(windowToken, false);
if (shouldHideSoftInput) {
@@ -3575,7 +3578,7 @@
return InputBindResult.USER_SWITCHING;
}
final int[] profileIdsWithDisabled = mUserManagerInternal.getProfileIds(
- mSettings.getUserId(), false /* enabledOnly */);
+ mCurrentUserId, false /* enabledOnly */);
for (int profileId : profileIdsWithDisabled) {
if (profileId == userId) {
scheduleSwitchUserTaskLocked(userId, cs.mClient);
@@ -3621,10 +3624,10 @@
}
// Verify if caller is a background user.
- final int currentUserId = mSettings.getUserId();
- if (userId != currentUserId) {
+ if (userId != mCurrentUserId) {
if (ArrayUtils.contains(
- mUserManagerInternal.getProfileIds(currentUserId, false), userId)) {
+ mUserManagerInternal.getProfileIds(mCurrentUserId, false),
+ userId)) {
// cross-profile access is always allowed here to allow
// profile-switching.
scheduleSwitchUserTaskLocked(userId, cs.mClient);
@@ -3813,7 +3816,7 @@
&& mImeBindingState.mFocusedWindowClient.mClient.asBinder() == client.asBinder()) {
return true;
}
- if (mSettings.getUserId() != UserHandle.getUserId(uid)) {
+ if (mCurrentUserId != UserHandle.getUserId(uid)) {
return false;
}
if (getCurIntentLocked() != null && InputMethodUtils.checkIfPackageBelongsToUid(
@@ -3844,13 +3847,11 @@
}
}
- @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
@Override
public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
// Always call subtype picker, because subtype picker is a superset of input method
// picker.
- super.showInputMethodPickerFromSystem_enforcePermission();
-
mHandler.obtainMessage(MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId)
.sendToTarget();
}
@@ -3858,10 +3859,8 @@
/**
* A test API for CTS to make sure that the input method menu is showing.
*/
- @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
public boolean isInputMethodPickerShownForTest() {
- super.isInputMethodPickerShownForTest_enforcePermission();
-
synchronized (ImfLock.class) {
return mMenuController.isisInputMethodPickerShownForTestLocked();
}
@@ -3881,9 +3880,10 @@
if (!calledWithValidTokenLocked(token)) {
return;
}
- final InputMethodInfo imi = mSettings.getMethodMap().get(id);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final InputMethodInfo imi = settings.getMethodMap().get(id);
if (imi == null || !canCallerAccessInputMethod(
- imi.getPackageName(), callingUid, userId, mSettings)) {
+ imi.getPackageName(), callingUid, userId, settings)) {
throw getExceptionForUnknownImeId(id);
}
setInputMethodWithSubtypeIdLocked(token, id, NOT_A_SUBTYPE_ID);
@@ -3899,9 +3899,10 @@
if (!calledWithValidTokenLocked(token)) {
return;
}
- final InputMethodInfo imi = mSettings.getMethodMap().get(id);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final InputMethodInfo imi = settings.getMethodMap().get(id);
if (imi == null || !canCallerAccessInputMethod(
- imi.getPackageName(), callingUid, userId, mSettings)) {
+ imi.getPackageName(), callingUid, userId, settings)) {
throw getExceptionForUnknownImeId(id);
}
if (subtype != null) {
@@ -3919,10 +3920,11 @@
if (!calledWithValidTokenLocked(token)) {
return false;
}
- final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtype();
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final Pair<String, String> lastIme = settings.getLastInputMethodAndSubtype();
final InputMethodInfo lastImi;
if (lastIme != null) {
- lastImi = mSettings.getMethodMap().get(lastIme.first);
+ lastImi = settings.getMethodMap().get(lastIme.first);
} else {
lastImi = null;
}
@@ -3946,7 +3948,7 @@
// This is a safety net. If the currentSubtype can't be added to the history
// and the framework couldn't find the last ime, we will make the last ime be
// the most applicable enabled keyboard subtype of the system imes.
- final List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodList();
+ final List<InputMethodInfo> enabled = settings.getEnabledInputMethodList();
if (enabled != null) {
final int enabledCount = enabled.size();
final String locale;
@@ -3954,7 +3956,7 @@
&& !TextUtils.isEmpty(mCurrentSubtype.getLocale())) {
locale = mCurrentSubtype.getLocale();
} else {
- locale = SystemLocaleWrapper.get(mSettings.getUserId()).get(0).toString();
+ locale = SystemLocaleWrapper.get(mCurrentUserId).get(0).toString();
}
for (int i = 0; i < enabledCount; ++i) {
final InputMethodInfo imi = enabled.get(i);
@@ -4001,8 +4003,9 @@
@GuardedBy("ImfLock.class")
private boolean switchToNextInputMethodLocked(@Nullable IBinder token, boolean onlyCurrentIme) {
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- onlyCurrentIme, mSettings.getMethodMap().get(getSelectedMethodIdLocked()),
+ onlyCurrentIme, settings.getMethodMap().get(getSelectedMethodIdLocked()),
mCurrentSubtype);
if (nextSubtype == null) {
return false;
@@ -4018,9 +4021,10 @@
if (!calledWithValidTokenLocked(token)) {
return false;
}
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
false /* onlyCurrentIme */,
- mSettings.getMethodMap().get(getSelectedMethodIdLocked()), mCurrentSubtype);
+ settings.getMethodMap().get(getSelectedMethodIdLocked()), mCurrentSubtype);
return nextSubtype != null;
}
}
@@ -4032,12 +4036,7 @@
Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
synchronized (ImfLock.class) {
- if (mSettings.getUserId() == userId) {
- return mSettings.getLastInputMethodSubtype();
- }
-
- final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
- return settings.getLastInputMethodSubtype();
+ return InputMethodSettingsRepository.get(userId).getLastInputMethodSubtype();
}
}
@@ -4068,23 +4067,20 @@
}
final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId);
- final boolean isCurrentUser = (mSettings.getUserId() == userId);
- final InputMethodSettings settings = isCurrentUser
- ? mSettings
- : queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
- DirectBootAwareness.AUTO);
+ final boolean isCurrentUser = (mCurrentUserId == userId);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
final var newAdditionalSubtypeMap = settings.getNewAdditionalSubtypeMap(
imiId, toBeAdded, additionalSubtypeMap, mPackageManagerInternal, callingUid);
if (additionalSubtypeMap != newAdditionalSubtypeMap) {
AdditionalSubtypeMapRepository.putAndSave(userId, newAdditionalSubtypeMap,
settings.getMethodMap());
+ final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
+ userId, AdditionalSubtypeMapRepository.get(userId),
+ DirectBootAwareness.AUTO);
+ InputMethodSettingsRepository.put(userId, newSettings);
if (isCurrentUser) {
final long ident = Binder.clearCallingIdentity();
try {
- mSettings = queryInputMethodServicesInternal(mContext,
- mSettings.getUserId(),
- AdditionalSubtypeMapRepository.get(mSettings.getUserId()),
- DirectBootAwareness.AUTO);
postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -4114,9 +4110,8 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (ImfLock.class) {
- final boolean currentUser = (mSettings.getUserId() == userId);
- final InputMethodSettings settings = currentUser
- ? mSettings : queryMethodMapForUserLocked(userId);
+ final boolean currentUser = (mCurrentUserId == userId);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
if (!settings.setEnabledInputMethodSubtypes(imeId, subtypeHashCodes)) {
return;
}
@@ -4161,11 +4156,9 @@
});
}
- @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
@Override
public void removeImeSurface() {
- super.removeImeSurface_enforcePermission();
-
mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
}
@@ -4274,11 +4267,9 @@
* a stylus deviceId is not already registered on device.
*/
@BinderThread
- @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
@Override
public void addVirtualStylusIdForTestSession(IInputMethodClient client) {
- super.addVirtualStylusIdForTestSession_enforcePermission();
-
int uid = Binder.getCallingUid();
synchronized (ImfLock.class) {
if (!canInteractWithImeLocked(uid, client, "addVirtualStylusIdForTestSession",
@@ -4301,12 +4292,10 @@
* @param timeout to set in milliseconds. To reset to default, use a value <= zero.
*/
@BinderThread
- @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
@Override
public void setStylusWindowIdleTimeoutForTest(
IInputMethodClient client, @DurationMillisLong long timeout) {
- super.setStylusWindowIdleTimeoutForTest_enforcePermission();
-
int uid = Binder.getCallingUid();
synchronized (ImfLock.class) {
if (!canInteractWithImeLocked(uid, client, "setStylusWindowIdleTimeoutForTest",
@@ -4402,10 +4391,9 @@
}
@BinderThread
- @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.CONTROL_UI_TRACING)
@Override
public void startImeTrace() {
- super.startImeTrace_enforcePermission();
ImeTracing.getInstance().startTrace(null /* printwriter */);
synchronized (ImfLock.class) {
mClientController.forAllClients(c -> c.mClient.setImeTraceEnabled(true /* enabled */));
@@ -4413,11 +4401,9 @@
}
@BinderThread
- @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.CONTROL_UI_TRACING)
@Override
public void stopImeTrace() {
- super.stopImeTrace_enforcePermission();
-
ImeTracing.getInstance().stopTrace(null /* printwriter */);
synchronized (ImfLock.class) {
mClientController.forAllClients(c -> c.mClient.setImeTraceEnabled(false /* enabled */));
@@ -4467,11 +4453,11 @@
}
return;
}
- if (mSettings.getUserId() != mSwitchingController.getUserId()) {
+ if (mCurrentUserId != mSwitchingController.getUserId()) {
return;
}
- final InputMethodInfo imi =
- mSettings.getMethodMap().get(getSelectedMethodIdLocked());
+ final InputMethodInfo imi = InputMethodSettingsRepository.get(mCurrentUserId)
+ .getMethodMap().get(getSelectedMethodIdLocked());
if (imi != null) {
mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
}
@@ -4531,8 +4517,9 @@
return;
} else {
// Called with current IME's token.
- if (mSettings.getMethodMap().get(id) != null
- && mSettings.getEnabledInputMethodListWithFilter(
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ if (settings.getMethodMap().get(id) != null
+ && settings.getEnabledInputMethodListWithFilter(
(info) -> info.getId().equals(id)).isEmpty()) {
throw new IllegalStateException("Requested IME is not enabled: " + id);
}
@@ -4697,7 +4684,7 @@
// implemented so that auxiliary subtypes will be excluded when the soft
// keyboard is invisible.
synchronized (ImfLock.class) {
- showAuxSubtypes = isInputShown();
+ showAuxSubtypes = isInputShownLocked();
}
break;
case InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES:
@@ -4711,21 +4698,23 @@
return false;
}
synchronized (ImfLock.class) {
+ final InputMethodSettings settings =
+ InputMethodSettingsRepository.get(mCurrentUserId);
final boolean isScreenLocked = mWindowManagerInternal.isKeyguardLocked()
- && mWindowManagerInternal.isKeyguardSecure(mSettings.getUserId());
- final String lastInputMethodId = mSettings.getSelectedInputMethod();
+ && mWindowManagerInternal.isKeyguardSecure(settings.getUserId());
+ final String lastInputMethodId = settings.getSelectedInputMethod();
int lastInputMethodSubtypeId =
- mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
+ settings.getSelectedInputMethodSubtypeId(lastInputMethodId);
final List<ImeSubtypeListItem> imList = InputMethodSubtypeSwitchingController
.getSortedInputMethodAndSubtypeList(
showAuxSubtypes, isScreenLocked, true /* forImeMenu */,
- mContext, mSettings.getMethodMap(), mSettings.getUserId());
+ mContext, settings.getMethodMap(), settings.getUserId());
if (imList.isEmpty()) {
Slog.w(TAG, "Show switching menu failed, imList is empty,"
+ " showAuxSubtypes: " + showAuxSubtypes
+ " isScreenLocked: " + isScreenLocked
- + " userId: " + mSettings.getUserId());
+ + " userId: " + settings.getUserId());
return false;
}
@@ -4911,8 +4900,9 @@
@GuardedBy("ImfLock.class")
private boolean chooseNewDefaultIMELocked() {
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
final InputMethodInfo imi = InputMethodInfoUtils.getMostApplicableDefaultIME(
- mSettings.getEnabledInputMethodList());
+ settings.getEnabledInputMethodList());
if (imi != null) {
if (DEBUG) {
Slog.d(TAG, "New default IME was selected: " + imi.getId());
@@ -5026,6 +5016,8 @@
mMethodMapUpdateCount++;
mMyPackageMonitor.clearKnownImePackageNamesLocked();
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+
// Construct the set of possible IME packages for onPackageChanged() to avoid false
// negatives when the package state remains to be the same but only the component state is
// changed.
@@ -5036,7 +5028,7 @@
final List<ResolveInfo> allInputMethodServices =
mContext.getPackageManager().queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE),
- PackageManager.MATCH_DISABLED_COMPONENTS, mSettings.getUserId());
+ PackageManager.MATCH_DISABLED_COMPONENTS, settings.getUserId());
final int numImes = allInputMethodServices.size();
for (int i = 0; i < numImes; ++i) {
final ServiceInfo si = allInputMethodServices.get(i).serviceInfo;
@@ -5051,11 +5043,11 @@
if (!resetDefaultEnabledIme) {
boolean enabledImeFound = false;
boolean enabledNonAuxImeFound = false;
- final List<InputMethodInfo> enabledImes = mSettings.getEnabledInputMethodList();
+ final List<InputMethodInfo> enabledImes = settings.getEnabledInputMethodList();
final int numImes = enabledImes.size();
for (int i = 0; i < numImes; ++i) {
final InputMethodInfo imi = enabledImes.get(i);
- if (mSettings.getMethodMap().containsKey(imi.getId())) {
+ if (settings.getMethodMap().containsKey(imi.getId())) {
enabledImeFound = true;
if (!imi.isAuxiliaryIme()) {
enabledNonAuxImeFound = true;
@@ -5079,7 +5071,7 @@
if (resetDefaultEnabledIme || reenableMinimumNonAuxSystemImes) {
final ArrayList<InputMethodInfo> defaultEnabledIme =
- InputMethodInfoUtils.getDefaultEnabledImes(mContext, mSettings.getMethodList(),
+ InputMethodInfoUtils.getDefaultEnabledImes(mContext, settings.getMethodList(),
reenableMinimumNonAuxSystemImes);
final int numImes = defaultEnabledIme.size();
for (int i = 0; i < numImes; ++i) {
@@ -5091,9 +5083,9 @@
}
}
- final String defaultImiId = mSettings.getSelectedInputMethod();
+ final String defaultImiId = settings.getSelectedInputMethod();
if (!TextUtils.isEmpty(defaultImiId)) {
- if (!mSettings.getMethodMap().containsKey(defaultImiId)) {
+ if (!settings.getMethodMap().containsKey(defaultImiId)) {
Slog.w(TAG, "Default IME is uninstalled. Choose new default IME.");
if (chooseNewDefaultIMELocked()) {
updateInputMethodsFromSettingsLocked(true);
@@ -5107,26 +5099,26 @@
updateDefaultVoiceImeIfNeededLocked();
// TODO: Instantiate mSwitchingController for each user.
- if (mSettings.getUserId() == mSwitchingController.getUserId()) {
- mSwitchingController.resetCircularListLocked(mSettings.getMethodMap());
+ if (settings.getUserId() == mSwitchingController.getUserId()) {
+ mSwitchingController.resetCircularListLocked(settings.getMethodMap());
} else {
mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
- mContext, mSettings.getMethodMap(), mSettings.getUserId());
+ mContext, settings.getMethodMap(), mCurrentUserId);
}
// TODO: Instantiate mHardwareKeyboardShortcutController for each user.
- if (mSettings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
- mHardwareKeyboardShortcutController.reset(mSettings.getMethodMap());
+ if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+ mHardwareKeyboardShortcutController.reset(settings.getMethodMap());
} else {
mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
- mSettings.getMethodMap(), mSettings.getUserId());
+ settings.getMethodMap(), settings.getUserId());
}
sendOnNavButtonFlagsChangedLocked();
// Notify InputMethodListListeners of the new installed InputMethods.
- final List<InputMethodInfo> inputMethodList = mSettings.getMethodList();
+ final List<InputMethodInfo> inputMethodList = settings.getMethodList();
mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED,
- mSettings.getUserId(), 0 /* unused */, inputMethodList).sendToTarget();
+ settings.getUserId(), 0 /* unused */, inputMethodList).sendToTarget();
}
@GuardedBy("ImfLock.class")
@@ -5141,11 +5133,12 @@
@GuardedBy("ImfLock.class")
private void updateDefaultVoiceImeIfNeededLocked() {
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
final String systemSpeechRecognizer =
mContext.getString(com.android.internal.R.string.config_systemSpeechRecognizer);
- final String currentDefaultVoiceImeId = mSettings.getDefaultVoiceInputMethod();
+ final String currentDefaultVoiceImeId = settings.getDefaultVoiceInputMethod();
final InputMethodInfo newSystemVoiceIme = InputMethodInfoUtils.chooseSystemVoiceIme(
- mSettings.getMethodMap(), systemSpeechRecognizer, currentDefaultVoiceImeId);
+ settings.getMethodMap(), systemSpeechRecognizer, currentDefaultVoiceImeId);
if (newSystemVoiceIme == null) {
if (DEBUG) {
Slog.i(TAG, "Found no valid default Voice IME. If the user is still locked,"
@@ -5154,7 +5147,7 @@
// Clear DEFAULT_VOICE_INPUT_METHOD when necessary. Note that InputMethodSettings
// does not update the actual Secure Settings until the user is unlocked.
if (!TextUtils.isEmpty(currentDefaultVoiceImeId)) {
- mSettings.putDefaultVoiceInputMethod("");
+ settings.putDefaultVoiceInputMethod("");
// We don't support disabling the voice ime when a package is removed from the
// config.
}
@@ -5167,7 +5160,7 @@
Slog.i(TAG, "Enabling the default Voice IME:" + newSystemVoiceIme);
}
setInputMethodEnabledLocked(newSystemVoiceIme.getId(), true);
- mSettings.putDefaultVoiceInputMethod(newSystemVoiceIme.getId());
+ settings.putDefaultVoiceInputMethod(newSystemVoiceIme.getId());
}
// ----------------------------------------------------------------------
@@ -5182,8 +5175,9 @@
*/
@GuardedBy("ImfLock.class")
private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
if (enabled) {
- final String enabledImeIdsStr = mSettings.getEnabledInputMethodsStr();
+ final String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
enabledImeIdsStr, id);
if (TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr)) {
@@ -5191,29 +5185,29 @@
// Nothing to do. The previous state was enabled.
return true;
}
- mSettings.putEnabledInputMethodsStr(newEnabledImeIdsStr);
+ settings.putEnabledInputMethodsStr(newEnabledImeIdsStr);
// Previous state was disabled.
return false;
} else {
- final List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
+ final List<Pair<String, ArrayList<String>>> enabledInputMethodsList = settings
.getEnabledInputMethodsAndSubtypeList();
StringBuilder builder = new StringBuilder();
- if (mSettings.buildAndPutEnabledInputMethodsStrRemovingId(
+ if (settings.buildAndPutEnabledInputMethodsStrRemovingId(
builder, enabledInputMethodsList, id)) {
if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
// Disabled input method is currently selected, switch to another one.
- final String selId = mSettings.getSelectedInputMethod();
+ final String selId = settings.getSelectedInputMethod();
if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
resetSelectedInputMethodAndSubtypeLocked("");
}
- } else if (id.equals(mSettings.getSelectedDefaultDeviceInputMethod())) {
+ } else if (id.equals(settings.getSelectedDefaultDeviceInputMethod())) {
// Disabled default device IME while using a virtual device one, choose a
// new default one but only update the settings.
InputMethodInfo newDefaultIme =
InputMethodInfoUtils.getMostApplicableDefaultIME(
- mSettings.getEnabledInputMethodList());
- mSettings.putSelectedDefaultDeviceInputMethod(
+ settings.getEnabledInputMethodList());
+ settings.putSelectedDefaultDeviceInputMethod(
newDefaultIme == null ? null : newDefaultIme.getId());
}
// Previous state was enabled.
@@ -5229,29 +5223,30 @@
@GuardedBy("ImfLock.class")
private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
boolean setSubtypeOnly) {
- mSettings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodIdLocked(),
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ settings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodIdLocked(),
mCurrentSubtype);
// Set Subtype here
if (imi == null || subtypeId < 0) {
- mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+ settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
mCurrentSubtype = null;
} else {
if (subtypeId < imi.getSubtypeCount()) {
InputMethodSubtype subtype = imi.getSubtypeAt(subtypeId);
- mSettings.putSelectedSubtype(subtype.hashCode());
+ settings.putSelectedSubtype(subtype.hashCode());
mCurrentSubtype = subtype;
} else {
- mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+ settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
// If the subtype is not specified, choose the most applicable one
mCurrentSubtype = getCurrentInputMethodSubtypeLocked();
}
}
- notifyInputMethodSubtypeChangedLocked(mSettings.getUserId(), imi, mCurrentSubtype);
+ notifyInputMethodSubtypeChangedLocked(settings.getUserId(), imi, mCurrentSubtype);
if (!setSubtypeOnly) {
// Set InputMethod here
- mSettings.putSelectedInputMethod(imi != null ? imi.getId() : "");
+ settings.putSelectedInputMethod(imi != null ? imi.getId() : "");
}
}
@@ -5259,13 +5254,15 @@
private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
mDisplayIdToShowIme = INVALID_DISPLAY;
- mSettings.putSelectedDefaultDeviceInputMethod(null);
- InputMethodInfo imi = mSettings.getMethodMap().get(newDefaultIme);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ settings.putSelectedDefaultDeviceInputMethod(null);
+
+ InputMethodInfo imi = settings.getMethodMap().get(newDefaultIme);
int lastSubtypeId = NOT_A_SUBTYPE_ID;
// newDefaultIme is empty when there is no candidate for the selected IME.
if (imi != null && !TextUtils.isEmpty(newDefaultIme)) {
- String subtypeHashCode = mSettings.getLastSubtypeForInputMethod(newDefaultIme);
+ String subtypeHashCode = settings.getLastSubtypeForInputMethod(newDefaultIme);
if (subtypeHashCode != null) {
try {
lastSubtypeId = SubtypeUtils.getSubtypeIdFromHashCode(imi,
@@ -5292,12 +5289,12 @@
Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
synchronized (ImfLock.class) {
- if (mSettings.getUserId() == userId) {
+ if (mCurrentUserId == userId) {
return getCurrentInputMethodSubtypeLocked();
}
- final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
- return settings.getCurrentInputMethodSubtypeForNonCurrentUsers();
+ return InputMethodSettingsRepository.get(userId)
+ .getCurrentInputMethodSubtypeForNonCurrentUsers();
}
}
@@ -5317,26 +5314,27 @@
if (selectedMethodId == null) {
return null;
}
- final boolean subtypeIsSelected = mSettings.isSubtypeSelected();
- final InputMethodInfo imi = mSettings.getMethodMap().get(selectedMethodId);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final boolean subtypeIsSelected = settings.isSubtypeSelected();
+ final InputMethodInfo imi = settings.getMethodMap().get(selectedMethodId);
if (imi == null || imi.getSubtypeCount() == 0) {
return null;
}
if (!subtypeIsSelected || mCurrentSubtype == null
|| !SubtypeUtils.isValidSubtypeId(imi, mCurrentSubtype.hashCode())) {
- int subtypeId = mSettings.getSelectedInputMethodSubtypeId(selectedMethodId);
+ int subtypeId = settings.getSelectedInputMethodSubtypeId(selectedMethodId);
if (subtypeId == NOT_A_SUBTYPE_ID) {
// If there are no selected subtypes, the framework will try to find
// the most applicable subtype from explicitly or implicitly enabled
// subtypes.
List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
- mSettings.getEnabledInputMethodSubtypeList(imi, true);
+ settings.getEnabledInputMethodSubtypeList(imi, true);
// If there is only one explicitly or implicitly enabled subtype,
// just returns it.
if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0);
} else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) {
- final String locale = SystemLocaleWrapper.get(mSettings.getUserId())
+ final String locale = SystemLocaleWrapper.get(settings.getUserId())
.get(0).toString();
mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype(
explicitlyOrImplicitlyEnabledSubtypes,
@@ -5359,38 +5357,22 @@
*/
@GuardedBy("ImfLock.class")
private InputMethodInfo queryDefaultInputMethodForUserIdLocked(@UserIdInt int userId) {
- final InputMethodSettings settings;
- if (userId == mSettings.getUserId()) {
- settings = mSettings;
- } else {
- final AdditionalSubtypeMap additionalSubtypeMap =
- AdditionalSubtypeMapRepository.get(userId);
- settings = queryInputMethodServicesInternal(mContext, userId,
- additionalSubtypeMap, DirectBootAwareness.AUTO);
- }
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
return settings.getMethodMap().get(settings.getSelectedInputMethod());
}
@GuardedBy("ImfLock.class")
- private InputMethodSettings queryMethodMapForUserLocked(@UserIdInt int userId) {
- final AdditionalSubtypeMap additionalSubtypeMap =
- AdditionalSubtypeMapRepository.get(userId);
- return queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
- DirectBootAwareness.AUTO);
- }
-
- @GuardedBy("ImfLock.class")
private boolean switchToInputMethodLocked(String imeId, @UserIdInt int userId) {
- if (userId == mSettings.getUserId()) {
- if (!mSettings.getMethodMap().containsKey(imeId)
- || !mSettings.getEnabledInputMethodList()
- .contains(mSettings.getMethodMap().get(imeId))) {
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
+ if (userId == mCurrentUserId) {
+ if (!settings.getMethodMap().containsKey(imeId)
+ || !settings.getEnabledInputMethodList()
+ .contains(settings.getMethodMap().get(imeId))) {
return false; // IME is not found or not enabled.
}
setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
return true;
}
- final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
if (!settings.getMethodMap().containsKey(imeId)
|| !settings.getEnabledInputMethodList().contains(
settings.getMethodMap().get(imeId))) {
@@ -5431,8 +5413,9 @@
@GuardedBy("ImfLock.class")
private void switchKeyboardLayoutLocked(int direction) {
- final InputMethodInfo currentImi = mSettings.getMethodMap().get(
- getSelectedMethodIdLocked());
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+
+ final InputMethodInfo currentImi = settings.getMethodMap().get(getSelectedMethodIdLocked());
if (currentImi == null) {
return;
}
@@ -5444,7 +5427,7 @@
if (nextSubtypeHandle == null) {
return;
}
- final InputMethodInfo nextImi = mSettings.getMethodMap().get(nextSubtypeHandle.getImeId());
+ final InputMethodInfo nextImi = settings.getMethodMap().get(nextSubtypeHandle.getImeId());
if (nextImi == null) {
return;
}
@@ -5523,17 +5506,14 @@
@Override
public boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) {
synchronized (ImfLock.class) {
- if (userId == mSettings.getUserId()) {
- if (!mSettings.getMethodMap().containsKey(imeId)) {
- return false; // IME is not found.
- }
- setInputMethodEnabledLocked(imeId, enabled);
- return true;
- }
- final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
if (!settings.getMethodMap().containsKey(imeId)) {
return false; // IME is not found.
}
+ if (userId == mCurrentUserId) {
+ setInputMethodEnabledLocked(imeId, enabled);
+ return true;
+ }
if (enabled) {
final String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
@@ -5844,7 +5824,7 @@
@BinderThread
@Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
PriorityDump.dump(mPriorityDumper, fd, pw, args);
@@ -5860,8 +5840,9 @@
final Printer p = new PrintWriterPrinter(pw);
synchronized (ImfLock.class) {
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
p.println("Current Input Method Manager state:");
- final List<InputMethodInfo> methodList = mSettings.getMethodList();
+ final List<InputMethodInfo> methodList = settings.getMethodList();
int numImes = methodList.size();
p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
for (int i = 0; i < numImes; i++) {
@@ -5885,6 +5866,7 @@
p.println(" curSession=" + c.mCurSession);
};
mClientController.forAllClients(clientControllerDump);
+ p.println(" mCurrentUserId=" + mCurrentUserId);
p.println(" mCurMethodId=" + getSelectedMethodIdLocked());
client = mCurClient;
p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked());
@@ -5910,8 +5892,6 @@
? Arrays.toString(mStylusIds.toArray()) : ""));
p.println(" mSwitchingController:");
mSwitchingController.dump(p);
- p.println(" mSettings:");
- mSettings.dump(p, " ");
p.println(" mStartInputHistory:");
mStartInputHistory.dump(pw, " ");
@@ -5974,7 +5954,7 @@
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@Nullable FileDescriptor err,
@NonNull String[] args, @Nullable ShellCallback callback,
- @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ @NonNull ResultReceiver resultReceiver, @NonNull Binder self) {
final int callingUid = Binder.getCallingUid();
// Reject any incoming calls from non-shell users, including ones from the system user.
if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) {
@@ -5995,7 +5975,7 @@
throw new SecurityException(errorMsg);
}
new ShellCommandImpl(this).exec(
- this, in, out, err, args, callback, resultReceiver);
+ self, in, out, err, args, callback, resultReceiver);
}
private static final class ShellCommandImpl extends ShellCommand {
@@ -6166,7 +6146,7 @@
}
synchronized (ImfLock.class) {
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
- mSettings.getUserId(), shellCommand.getErrPrintWriter());
+ mCurrentUserId, shellCommand.getErrPrintWriter());
try (PrintWriter pr = shellCommand.getOutPrintWriter()) {
for (int userId : userIds) {
final List<InputMethodInfo> methods = all
@@ -6211,7 +6191,7 @@
PrintWriter error = shellCommand.getErrPrintWriter()) {
synchronized (ImfLock.class) {
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
- mSettings.getUserId(), shellCommand.getErrPrintWriter());
+ mCurrentUserId, shellCommand.getErrPrintWriter());
for (int userId : userIds) {
if (!userHasDebugPriv(userId, shellCommand)) {
continue;
@@ -6270,14 +6250,14 @@
PrintWriter error) {
boolean failedToEnableUnknownIme = false;
boolean previouslyEnabled = false;
- if (userId == mSettings.getUserId()) {
- if (enabled && !mSettings.getMethodMap().containsKey(imeId)) {
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
+ if (userId == mCurrentUserId) {
+ if (enabled && !settings.getMethodMap().containsKey(imeId)) {
failedToEnableUnknownIme = true;
} else {
previouslyEnabled = setInputMethodEnabledLocked(imeId, enabled);
}
} else {
- final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
if (enabled) {
if (!settings.getMethodMap().containsKey(imeId)) {
failedToEnableUnknownIme = true;
@@ -6332,7 +6312,7 @@
PrintWriter error = shellCommand.getErrPrintWriter()) {
synchronized (ImfLock.class) {
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
- mSettings.getUserId(), shellCommand.getErrPrintWriter());
+ mCurrentUserId, shellCommand.getErrPrintWriter());
for (int userId : userIds) {
if (!userHasDebugPriv(userId, shellCommand)) {
continue;
@@ -6372,7 +6352,7 @@
synchronized (ImfLock.class) {
try (PrintWriter out = shellCommand.getOutPrintWriter()) {
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
- mSettings.getUserId(), shellCommand.getErrPrintWriter());
+ mCurrentUserId, shellCommand.getErrPrintWriter());
for (int userId : userIds) {
if (!userHasDebugPriv(userId, shellCommand)) {
continue;
@@ -6384,15 +6364,16 @@
}
final String nextIme;
final List<InputMethodInfo> nextEnabledImes;
- if (userId == mSettings.getUserId()) {
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
+ if (userId == mCurrentUserId) {
hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
mBindingController.unbindCurrentMethod();
// Enable default IMEs, disable others
- var toDisable = mSettings.getEnabledInputMethodList();
+ var toDisable = settings.getEnabledInputMethodList();
var defaultEnabled = InputMethodInfoUtils.getDefaultEnabledImes(
- mContext, mSettings.getMethodList());
+ mContext, settings.getMethodList());
toDisable.removeAll(defaultEnabled);
for (InputMethodInfo info : toDisable) {
setInputMethodEnabledLocked(info.getId(), false);
@@ -6406,16 +6387,11 @@
}
updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
- getPackageManagerForUser(mContext, mSettings.getUserId()),
- mSettings.getEnabledInputMethodList());
- nextIme = mSettings.getSelectedInputMethod();
- nextEnabledImes = mSettings.getEnabledInputMethodList();
+ getPackageManagerForUser(mContext, settings.getUserId()),
+ settings.getEnabledInputMethodList());
+ nextIme = settings.getSelectedInputMethod();
+ nextEnabledImes = settings.getEnabledInputMethodList();
} else {
- final AdditionalSubtypeMap additionalSubtypeMap =
- AdditionalSubtypeMapRepository.get(userId);
- final InputMethodSettings settings = queryInputMethodServicesInternal(
- mContext, userId, additionalSubtypeMap, DirectBootAwareness.AUTO);
-
nextEnabledImes = InputMethodInfoUtils.getDefaultEnabledImes(mContext,
settings.getMethodList());
nextIme = InputMethodInfoUtils.getMostApplicableDefaultIME(
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java b/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java
new file mode 100644
index 0000000..60b9a4c
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.Handler;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+
+final class InputMethodSettingsRepository {
+ @GuardedBy("ImfLock.class")
+ @NonNull
+ private static final SparseArray<InputMethodSettings> sPerUserMap = new SparseArray<>();
+
+ /**
+ * Not intended to be instantiated.
+ */
+ private InputMethodSettingsRepository() {
+ }
+
+ @NonNull
+ @GuardedBy("ImfLock.class")
+ static InputMethodSettings get(@UserIdInt int userId) {
+ final InputMethodSettings obj = sPerUserMap.get(userId);
+ if (obj != null) {
+ return obj;
+ }
+ return InputMethodSettings.createEmptyMap(userId);
+ }
+
+ @GuardedBy("ImfLock.class")
+ static void put(@UserIdInt int userId, @NonNull InputMethodSettings obj) {
+ sPerUserMap.put(userId, obj);
+ }
+
+ static void initialize(@NonNull Handler handler, @NonNull Context context) {
+ final UserManagerInternal userManagerInternal =
+ LocalServices.getService(UserManagerInternal.class);
+ handler.post(() -> {
+ userManagerInternal.addUserLifecycleListener(
+ new UserManagerInternal.UserLifecycleListener() {
+ @Override
+ public void onUserRemoved(UserInfo user) {
+ final int userId = user.id;
+ handler.post(() -> {
+ synchronized (ImfLock.class) {
+ sPerUserMap.remove(userId);
+ }
+ });
+ }
+ });
+ synchronized (ImfLock.class) {
+ for (int userId : userManagerInternal.getUserIds()) {
+ final InputMethodSettings settings =
+ InputMethodManagerService.queryInputMethodServicesInternal(
+ context,
+ userId,
+ AdditionalSubtypeMapRepository.get(userId),
+ DirectBootAwareness.AUTO);
+ sPerUserMap.put(userId, settings);
+ }
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index ffc2319..1cd1ddc 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -36,17 +36,16 @@
import android.Manifest;
import android.annotation.BinderThread;
-import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.os.Binder;
import android.os.IBinder;
-import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.util.Slog;
+import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
@@ -56,6 +55,7 @@
import android.view.inputmethod.InputMethodSubtype;
import android.window.ImeOnBackInvokedDispatcher;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
@@ -76,22 +76,25 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
-import java.util.function.BooleanSupplier;
/**
* A proxy that processes all {@link IInputMethodManager} calls asynchronously.
- * @hide
*/
-public class ZeroJankProxy extends IInputMethodManager.Stub {
+final class ZeroJankProxy implements IInputMethodManagerImpl.Callback {
- private final IInputMethodManager mInner;
+ interface Callback extends IInputMethodManagerImpl.Callback {
+ @GuardedBy("ImfLock.class")
+ ClientState getClientStateLocked(IInputMethodClient client);
+ @GuardedBy("ImfLock.class")
+ boolean isInputShownLocked();
+ }
+
+ private final Callback mInner;
private final Executor mExecutor;
- private final BooleanSupplier mIsInputShown;
- ZeroJankProxy(Executor executor, IInputMethodManager inner, BooleanSupplier isInputShown) {
+ ZeroJankProxy(Executor executor, Callback inner) {
mInner = inner;
mExecutor = executor;
- mIsInputShown = isInputShown;
}
private void offload(ThrowingRunnable r) {
@@ -126,45 +129,43 @@
@Override
public void addClient(IInputMethodClient client, IRemoteInputConnection inputConnection,
- int selfReportedDisplayId) throws RemoteException {
+ int selfReportedDisplayId) {
offload(() -> mInner.addClient(client, inputConnection, selfReportedDisplayId));
}
@Override
- public InputMethodInfo getCurrentInputMethodInfoAsUser(int userId) throws RemoteException {
+ public InputMethodInfo getCurrentInputMethodInfoAsUser(int userId) {
return mInner.getCurrentInputMethodInfoAsUser(userId);
}
@Override
public List<InputMethodInfo> getInputMethodList(
- int userId, @DirectBootAwareness int directBootAwareness) throws RemoteException {
+ int userId, @DirectBootAwareness int directBootAwareness) {
return mInner.getInputMethodList(userId, directBootAwareness);
}
@Override
- public List<InputMethodInfo> getEnabledInputMethodList(int userId) throws RemoteException {
+ public List<InputMethodInfo> getEnabledInputMethodList(int userId) {
return mInner.getEnabledInputMethodList(userId);
}
@Override
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
- boolean allowsImplicitlyEnabledSubtypes, int userId)
- throws RemoteException {
+ boolean allowsImplicitlyEnabledSubtypes, int userId) {
return mInner.getEnabledInputMethodSubtypeList(imiId, allowsImplicitlyEnabledSubtypes,
userId);
}
@Override
- public InputMethodSubtype getLastInputMethodSubtype(int userId) throws RemoteException {
+ public InputMethodSubtype getLastInputMethodSubtype(int userId) {
return mInner.getLastInputMethodSubtype(userId);
}
@Override
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
- int lastClickTooType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason)
- throws RemoteException {
+ @MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
offload(
() -> {
if (!mInner.showSoftInput(
@@ -172,7 +173,7 @@
windowToken,
statsToken,
flags,
- lastClickTooType,
+ lastClickToolType,
resultReceiver,
reason)) {
sendResultReceiverFailure(resultReceiver);
@@ -184,8 +185,7 @@
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)
- throws RemoteException {
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
offload(
() -> {
if (!mInner.hideSoftInput(
@@ -200,17 +200,19 @@
if (resultReceiver == null) {
return;
}
- resultReceiver.send(
- mIsInputShown.getAsBoolean()
+ final boolean isInputShown;
+ synchronized (ImfLock.class) {
+ isInputShown = mInner.isInputShownLocked();
+ }
+ resultReceiver.send(isInputShown
? InputMethodManager.RESULT_UNCHANGED_SHOWN
: InputMethodManager.RESULT_UNCHANGED_HIDDEN,
null);
}
@Override
- @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
- public void hideSoftInputFromServerForTest() throws RemoteException {
- super.hideSoftInputFromServerForTest_enforcePermission();
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
+ public void hideSoftInputFromServerForTest() {
mInner.hideSoftInputFromServerForTest();
}
@@ -225,8 +227,7 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq)
- throws RemoteException {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
offload(() -> {
InputBindResult result = mInner.startInputOrWindowGainedFocus(startInputReason, client,
windowToken, startInputFlags, softInputMode, windowFlags,
@@ -249,99 +250,92 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher)
- throws RemoteException {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
// Should never be called when flag is enabled i.e. when this proxy is used.
return null;
}
@Override
public void showInputMethodPickerFromClient(IInputMethodClient client,
- int auxiliarySubtypeMode)
- throws RemoteException {
+ int auxiliarySubtypeMode) {
offload(() -> mInner.showInputMethodPickerFromClient(client, auxiliarySubtypeMode));
}
- @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
@Override
- public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId)
- throws RemoteException {
+ public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
mInner.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
}
- @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
@Override
- public boolean isInputMethodPickerShownForTest() throws RemoteException {
- super.isInputMethodPickerShownForTest_enforcePermission();
+ public boolean isInputMethodPickerShownForTest() {
return mInner.isInputMethodPickerShownForTest();
}
@Override
- public InputMethodSubtype getCurrentInputMethodSubtype(int userId) throws RemoteException {
+ public InputMethodSubtype getCurrentInputMethodSubtype(int userId) {
return mInner.getCurrentInputMethodSubtype(userId);
}
@Override
public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
- @UserIdInt int userId) throws RemoteException {
+ @UserIdInt int userId) {
mInner.setAdditionalInputMethodSubtypes(imiId, subtypes, userId);
}
@Override
public void setExplicitlyEnabledInputMethodSubtypes(String imeId,
- @NonNull int[] subtypeHashCodes, @UserIdInt int userId) throws RemoteException {
+ @NonNull int[] subtypeHashCodes, @UserIdInt int userId) {
mInner.setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes, userId);
}
@Override
- public int getInputMethodWindowVisibleHeight(IInputMethodClient client)
- throws RemoteException {
+ public int getInputMethodWindowVisibleHeight(IInputMethodClient client) {
return mInner.getInputMethodWindowVisibleHeight(client);
}
@Override
- public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible)
- throws RemoteException {
+ public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
// Already async TODO(b/293640003): ordering issues?
mInner.reportPerceptibleAsync(windowToken, perceptible);
}
- @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
@Override
- public void removeImeSurface() throws RemoteException {
+ public void removeImeSurface() {
mInner.removeImeSurface();
}
@Override
- public void removeImeSurfaceFromWindowAsync(IBinder windowToken) throws RemoteException {
+ public void removeImeSurfaceFromWindowAsync(IBinder windowToken) {
mInner.removeImeSurfaceFromWindowAsync(windowToken);
}
@Override
- public void startProtoDump(byte[] bytes, int i, String s) throws RemoteException {
+ public void startProtoDump(byte[] bytes, int i, String s) {
mInner.startProtoDump(bytes, i, s);
}
@Override
- public boolean isImeTraceEnabled() throws RemoteException {
+ public boolean isImeTraceEnabled() {
return mInner.isImeTraceEnabled();
}
- @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.CONTROL_UI_TRACING)
@Override
- public void startImeTrace() throws RemoteException {
+ public void startImeTrace() {
mInner.startImeTrace();
}
- @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.CONTROL_UI_TRACING)
@Override
- public void stopImeTrace() throws RemoteException {
+ public void stopImeTrace() {
mInner.stopImeTrace();
}
@Override
- public void startStylusHandwriting(IInputMethodClient client)
- throws RemoteException {
+ public void startStylusHandwriting(IInputMethodClient client) {
offload(() -> mInner.startStylusHandwriting(client));
}
@@ -349,7 +343,7 @@
public void startConnectionlessStylusHandwriting(IInputMethodClient client, int userId,
@Nullable CursorAnchorInfo cursorAnchorInfo, @Nullable String delegatePackageName,
@Nullable String delegatorPackageName,
- @NonNull IConnectionlessHandwritingCallback callback) throws RemoteException {
+ @NonNull IConnectionlessHandwritingCallback callback) {
offload(() -> mInner.startConnectionlessStylusHandwriting(
client, userId, cursorAnchorInfo, delegatePackageName, delegatorPackageName,
callback));
@@ -363,14 +357,11 @@
@NonNull String delegatorPackageName,
@InputMethodManager.HandwritingDelegateFlags int flags) {
try {
- return CompletableFuture.supplyAsync(() -> {
- try {
- return mInner.acceptStylusHandwritingDelegation(
- client, userId, delegatePackageName, delegatorPackageName, flags);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }, this::offload).get();
+ return CompletableFuture.supplyAsync(() ->
+ mInner.acceptStylusHandwritingDelegation(
+ client, userId, delegatePackageName, delegatorPackageName,
+ flags),
+ this::offload).get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
@@ -384,8 +375,7 @@
@UserIdInt int userId,
@NonNull String delegatePackageName,
@NonNull String delegatorPackageName,
- @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback)
- throws RemoteException {
+ @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback) {
offload(() -> mInner.acceptStylusHandwritingDelegationAsync(
client, userId, delegatePackageName, delegatorPackageName, flags, callback));
}
@@ -401,52 +391,45 @@
}
@Override
- public boolean isStylusHandwritingAvailableAsUser(int userId, boolean connectionless)
- throws RemoteException {
+ public boolean isStylusHandwritingAvailableAsUser(int userId, boolean connectionless) {
return mInner.isStylusHandwritingAvailableAsUser(userId, connectionless);
}
- @EnforcePermission("android.permission.TEST_INPUT_METHOD")
+ @IInputMethodManagerImpl.PermissionVerified("android.permission.TEST_INPUT_METHOD")
@Override
- public void addVirtualStylusIdForTestSession(IInputMethodClient client)
- throws RemoteException {
+ public void addVirtualStylusIdForTestSession(IInputMethodClient client) {
mInner.addVirtualStylusIdForTestSession(client);
}
- @EnforcePermission("android.permission.TEST_INPUT_METHOD")
+ @IInputMethodManagerImpl.PermissionVerified("android.permission.TEST_INPUT_METHOD")
@Override
- public void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout)
- throws RemoteException {
+ public void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout) {
mInner.setStylusWindowIdleTimeoutForTest(client, timeout);
}
@Override
- public IImeTracker getImeTrackerService() throws RemoteException {
+ public IImeTracker getImeTrackerService() {
return mInner.getImeTrackerService();
}
@BinderThread
@Override
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
- @Nullable FileDescriptor err,
- @NonNull String[] args, @Nullable ShellCallback callback,
- @NonNull ResultReceiver resultReceiver) throws RemoteException {
- ((InputMethodManagerService) mInner).onShellCommand(
- in, out, err, args, callback, resultReceiver);
+ @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver, @NonNull Binder self) {
+ mInner.onShellCommand(in, out, err, args, callback, resultReceiver, self);
}
@Override
- protected void dump(@NonNull FileDescriptor fd,
- @NonNull PrintWriter fout,
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {
- ((InputMethodManagerService) mInner).dump(fd, fout, args);
+ mInner.dump(fd, fout, args);
}
private void sendOnStartInputResult(
IInputMethodClient client, InputBindResult res, int startInputSeq) {
synchronized (ImfLock.class) {
- InputMethodManagerService service = (InputMethodManagerService) mInner;
- final ClientState cs = service.getClientState(client);
+ final ClientState cs = mInner.getClientStateLocked(client);
if (cs != null && cs.mClient != null) {
cs.mClient.onStartInputResult(res, startInputSeq);
} else {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 19562ef..dbdb155 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -950,13 +950,18 @@
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) {
mHandler.post(() -> {
- UserProperties userProperties =
- mUserManager.getUserProperties(UserHandle.of(userId));
- if (userProperties != null
- && userProperties.getAllowStoppingUserWithDelayedLocking()) {
- int strongAuthRequired = LockPatternUtils.StrongAuthTracker
- .getDefaultFlags(mContext);
- requireStrongAuth(strongAuthRequired, userId);
+ try {
+ UserProperties userProperties =
+ mUserManager.getUserProperties(UserHandle.of(userId));
+ if (userProperties != null && userProperties
+ .getAllowStoppingUserWithDelayedLocking()) {
+ int strongAuthRequired = LockPatternUtils.StrongAuthTracker
+ .getDefaultFlags(mContext);
+ requireStrongAuth(strongAuthRequired, userId);
+ }
+ } catch (IllegalArgumentException e) {
+ Slogf.d(TAG, "User %d does not exist or has been removed",
+ userId);
}
});
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 1a129cb..064443c 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -267,9 +267,11 @@
// Binder call
@Override
public boolean showMediaOutputSwitcher(String packageName) {
- if (!validatePackageName(Binder.getCallingUid(), packageName)) {
+ int uid = Binder.getCallingUid();
+ if (!validatePackageName(uid, packageName)) {
throw new SecurityException("packageName must match the calling identity");
}
+ UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
final long token = Binder.clearCallingIdentity();
try {
if (mContext.getSystemService(ActivityManager.class).getPackageImportance(packageName)
@@ -280,7 +282,7 @@
synchronized (mLock) {
StatusBarManagerInternal statusBar =
LocalServices.getService(StatusBarManagerInternal.class);
- statusBar.showMediaOutputSwitcher(packageName);
+ statusBar.showMediaOutputSwitcher(packageName, userHandle);
}
} finally {
Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 0cd7654..dfb2b0a 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -157,6 +157,11 @@
}
@Override
+ public void expireTempEngaged() {
+ // NA as MediaSession2 doesn't support UserEngagementStates for FGS.
+ }
+
+ @Override
public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
KeyEvent ke, int sequenceId, ResultReceiver cb) {
// TODO(jaewan): Implement.
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 62a9471..3d68555 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -24,6 +24,7 @@
import static android.media.session.MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -85,6 +86,8 @@
import com.android.server.uri.UriGrantsManagerInternal;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -225,6 +228,49 @@
private int mPolicies;
+ private @UserEngagementState int mUserEngagementState = USER_DISENGAGED;
+
+ @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARY_ENGAGED, USER_DISENGAGED})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface UserEngagementState {}
+
+ /**
+ * Indicates that the session is active and in one of the user engaged states.
+ *
+ * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
+ */
+ private static final int USER_PERMANENTLY_ENGAGED = 0;
+
+ /**
+ * Indicates that the session is active and in {@link PlaybackState#STATE_PAUSED} state.
+ *
+ * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
+ */
+ private static final int USER_TEMPORARY_ENGAGED = 1;
+
+ /**
+ * Indicates that the session is either not active or in one of the user disengaged states
+ *
+ * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
+ */
+ private static final int USER_DISENGAGED = 2;
+
+ /**
+ * Indicates the duration of the temporary engaged states.
+ *
+ * <p>Some {@link MediaSession} states like {@link PlaybackState#STATE_PAUSED} are temporarily
+ * engaged, meaning the corresponding session is only considered in an engaged state for the
+ * duration of this timeout, and only if coming from an engaged state.
+ *
+ * <p>For example, if a session is transitioning from a user-engaged state {@link
+ * PlaybackState#STATE_PLAYING} to a temporary user-engaged state {@link
+ * PlaybackState#STATE_PAUSED}, then the session will be considered in a user-engaged state for
+ * the duration of this timeout, starting at the transition instant. However, a temporary
+ * user-engaged state is not considered user-engaged when transitioning from a non-user engaged
+ * state {@link PlaybackState#STATE_STOPPED}.
+ */
+ private static final int TEMP_USER_ENGAGED_TIMEOUT = 600000;
+
public MediaSessionRecord(
int ownerPid,
int ownerUid,
@@ -548,6 +594,7 @@
mSessionCb.mCb.asBinder().unlinkToDeath(this, 0);
mDestroyed = true;
mPlaybackState = null;
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
mHandler.post(MessageHandler.MSG_DESTROYED);
}
}
@@ -559,6 +606,12 @@
}
}
+ @Override
+ public void expireTempEngaged() {
+ mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout);
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+ }
+
/**
* Sends media button.
*
@@ -1129,6 +1182,11 @@
}
};
+ private final Runnable mHandleTempEngagedSessionTimeout =
+ () -> {
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+ };
+
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
private static boolean componentNameExists(
@NonNull ComponentName componentName, @NonNull Context context, int userId) {
@@ -1145,6 +1203,40 @@
return !resolveInfos.isEmpty();
}
+ private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) {
+ int oldUserEngagedState = mUserEngagementState;
+ int newUserEngagedState;
+ if (!isActive() || mPlaybackState == null) {
+ newUserEngagedState = USER_DISENGAGED;
+ } else if (isActive() && mPlaybackState.isActive()) {
+ newUserEngagedState = USER_PERMANENTLY_ENGAGED;
+ } else if (mPlaybackState.getState() == PlaybackState.STATE_PAUSED) {
+ newUserEngagedState =
+ oldUserEngagedState == USER_PERMANENTLY_ENGAGED || !isTimeoutExpired
+ ? USER_TEMPORARY_ENGAGED
+ : USER_DISENGAGED;
+ } else {
+ newUserEngagedState = USER_DISENGAGED;
+ }
+ if (oldUserEngagedState == newUserEngagedState) {
+ return;
+ }
+
+ if (newUserEngagedState == USER_TEMPORARY_ENGAGED) {
+ mHandler.postDelayed(mHandleTempEngagedSessionTimeout, TEMP_USER_ENGAGED_TIMEOUT);
+ } else if (oldUserEngagedState == USER_TEMPORARY_ENGAGED) {
+ mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout);
+ }
+
+ boolean wasUserEngaged = oldUserEngagedState != USER_DISENGAGED;
+ boolean isNowUserEngaged = newUserEngagedState != USER_DISENGAGED;
+ mUserEngagementState = newUserEngagedState;
+ if (wasUserEngaged != isNowUserEngaged) {
+ mService.onSessionUserEngagementStateChange(
+ /* mediaSessionRecord= */ this, /* isUserEngaged= */ isNowUserEngaged);
+ }
+ }
+
private final class SessionStub extends ISession.Stub {
@Override
public void destroySession() throws RemoteException {
@@ -1182,8 +1274,10 @@
.logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK,
callingUid, callingPid);
}
-
- mIsActive = active;
+ synchronized (mLock) {
+ mIsActive = active;
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
+ }
long token = Binder.clearCallingIdentity();
try {
mService.onSessionActiveStateChanged(MediaSessionRecord.this, mPlaybackState);
@@ -1341,6 +1435,7 @@
&& TRANSITION_PRIORITY_STATES.contains(newState));
synchronized (mLock) {
mPlaybackState = state;
+ updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
}
final long token = Binder.clearCallingIdentity();
try {
@@ -1362,12 +1457,14 @@
@Override
public IBinder getBinderForSetQueue() throws RemoteException {
- return new ParcelableListBinder<QueueItem>((list) -> {
- synchronized (mLock) {
- mQueue = list;
- }
- mHandler.post(MessageHandler.MSG_UPDATE_QUEUE);
- });
+ return new ParcelableListBinder<QueueItem>(
+ QueueItem.class,
+ (list) -> {
+ synchronized (mLock) {
+ mQueue = list;
+ }
+ mHandler.post(MessageHandler.MSG_UPDATE_QUEUE);
+ });
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 0999199..b57b148 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -196,6 +196,12 @@
*/
public abstract boolean isClosed();
+ /**
+ * Note: This method is only used for testing purposes If the session is temporary engaged, the
+ * timeout will expire and it will become disengaged.
+ */
+ public abstract void expireTempEngaged();
+
@Override
public final boolean equals(Object o) {
if (this == o) return true;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 53c32cf..74adf5e 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -367,11 +367,13 @@
}
boolean isUserEngaged = isUserEngaged(record, playbackState);
- Log.d(TAG, "onSessionActiveStateChanged: "
- + "record=" + record
- + "playbackState=" + playbackState
- + "allowRunningInForeground=" + isUserEngaged);
- setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged);
+ Log.d(
+ TAG,
+ "onSessionActiveStateChanged:"
+ + " record="
+ + record
+ + " playbackState="
+ + playbackState);
reportMediaInteractionEvent(record, isUserEngaged);
mHandler.postSessionsChanged(record);
}
@@ -479,11 +481,13 @@
}
user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
boolean isUserEngaged = isUserEngaged(record, playbackState);
- Log.d(TAG, "onSessionPlaybackStateChanged: "
- + "record=" + record
- + "playbackState=" + playbackState
- + "allowRunningInForeground=" + isUserEngaged);
- setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged);
+ Log.d(
+ TAG,
+ "onSessionPlaybackStateChanged:"
+ + " record="
+ + record
+ + " playbackState="
+ + playbackState);
reportMediaInteractionEvent(record, isUserEngaged);
}
}
@@ -650,68 +654,112 @@
session.close();
Log.d(TAG, "destroySessionLocked: record=" + session);
- setForegroundServiceAllowance(session, /* allowRunningInForeground= */ false);
+
reportMediaInteractionEvent(session, /* userEngaged= */ false);
mHandler.postSessionsChanged(session);
}
- private void setForegroundServiceAllowance(
- MediaSessionRecordImpl record, boolean allowRunningInForeground) {
- if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
- return;
- }
- ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
- record.getForegroundServiceDelegationOptions();
- if (foregroundServiceDelegationOptions == null) {
- return;
- }
- if (allowRunningInForeground) {
- onUserSessionEngaged(record);
+ void onSessionUserEngagementStateChange(
+ MediaSessionRecordImpl mediaSessionRecord, boolean isUserEngaged) {
+ if (isUserEngaged) {
+ addUserEngagedSession(mediaSessionRecord);
+ startFgsIfSessionIsLinkedToNotification(mediaSessionRecord);
} else {
- onUserDisengaged(record);
+ removeUserEngagedSession(mediaSessionRecord);
+ stopFgsIfNoSessionIsLinkedToNotification(mediaSessionRecord);
}
}
- private void onUserSessionEngaged(MediaSessionRecordImpl mediaSessionRecord) {
+ private void addUserEngagedSession(MediaSessionRecordImpl mediaSessionRecord) {
synchronized (mLock) {
int uid = mediaSessionRecord.getUid();
mUserEngagedSessionsForFgs.putIfAbsent(uid, new HashSet<>());
mUserEngagedSessionsForFgs.get(uid).add(mediaSessionRecord);
+ }
+ }
+
+ private void removeUserEngagedSession(MediaSessionRecordImpl mediaSessionRecord) {
+ synchronized (mLock) {
+ int uid = mediaSessionRecord.getUid();
+ Set<MediaSessionRecordImpl> mUidUserEngagedSessionsForFgs =
+ mUserEngagedSessionsForFgs.get(uid);
+ if (mUidUserEngagedSessionsForFgs == null) {
+ return;
+ }
+
+ mUidUserEngagedSessionsForFgs.remove(mediaSessionRecord);
+ if (mUidUserEngagedSessionsForFgs.isEmpty()) {
+ mUserEngagedSessionsForFgs.remove(uid);
+ }
+ }
+ }
+
+ private void startFgsIfSessionIsLinkedToNotification(
+ MediaSessionRecordImpl mediaSessionRecord) {
+ Log.d(TAG, "startFgsIfSessionIsLinkedToNotification: record=" + mediaSessionRecord);
+ if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+ return;
+ }
+ synchronized (mLock) {
+ int uid = mediaSessionRecord.getUid();
for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) {
if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) {
- mActivityManagerInternal.startForegroundServiceDelegate(
- mediaSessionRecord.getForegroundServiceDelegationOptions(),
- /* connection= */ null);
+ startFgsDelegate(mediaSessionRecord.getForegroundServiceDelegationOptions());
return;
}
}
}
}
- private void onUserDisengaged(MediaSessionRecordImpl mediaSessionRecord) {
+ private void startFgsDelegate(
+ ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mActivityManagerInternal.startForegroundServiceDelegate(
+ foregroundServiceDelegationOptions, /* connection= */ null);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void stopFgsIfNoSessionIsLinkedToNotification(
+ MediaSessionRecordImpl mediaSessionRecord) {
+ Log.d(TAG, "stopFgsIfNoSessionIsLinkedToNotification: record=" + mediaSessionRecord);
+ if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+ return;
+ }
synchronized (mLock) {
int uid = mediaSessionRecord.getUid();
- if (mUserEngagedSessionsForFgs.containsKey(uid)) {
- mUserEngagedSessionsForFgs.get(uid).remove(mediaSessionRecord);
- if (mUserEngagedSessionsForFgs.get(uid).isEmpty()) {
- mUserEngagedSessionsForFgs.remove(uid);
- }
+ ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
+ mediaSessionRecord.getForegroundServiceDelegationOptions();
+ if (foregroundServiceDelegationOptions == null) {
+ return;
}
- boolean shouldStopFgs = true;
- for (MediaSessionRecordImpl sessionRecord :
+ for (MediaSessionRecordImpl record :
mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
- for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid,
- Set.of())) {
- if (sessionRecord.isLinkedToNotification(mediaNotification)) {
- shouldStopFgs = false;
+ for (Notification mediaNotification :
+ mMediaNotifications.getOrDefault(uid, Set.of())) {
+ if (record.isLinkedToNotification(mediaNotification)) {
+ // A user engaged session linked with a media notification is found.
+ // We shouldn't call stop FGS in this case.
+ return;
}
}
}
- if (shouldStopFgs) {
- mActivityManagerInternal.stopForegroundServiceDelegate(
- mediaSessionRecord.getForegroundServiceDelegationOptions());
- }
+
+ stopFgsDelegate(foregroundServiceDelegationOptions);
+ }
+ }
+
+ private void stopFgsDelegate(
+ ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mActivityManagerInternal.stopForegroundServiceDelegate(
+ foregroundServiceDelegationOptions);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -2502,7 +2550,6 @@
}
MediaSessionRecord session = null;
MediaButtonReceiverHolder mediaButtonReceiverHolder = null;
-
if (mCustomMediaKeyDispatcher != null) {
MediaSession.Token token = mCustomMediaKeyDispatcher.getMediaSession(
keyEvent, uid, asSystemService);
@@ -2630,6 +2677,18 @@
&& streamType <= AudioManager.STREAM_NOTIFICATION;
}
+ @Override
+ public void expireTempEngagedSessions() {
+ synchronized (mLock) {
+ for (Set<MediaSessionRecordImpl> uidSessions :
+ mUserEngagedSessionsForFgs.values()) {
+ for (MediaSessionRecordImpl sessionRecord : uidSessions) {
+ sessionRecord.expireTempEngaged();
+ }
+ }
+ }
+ }
+
private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
private final String mPackageName;
private final int mPid;
@@ -3127,7 +3186,6 @@
super.onNotificationPosted(sbn);
Notification postedNotification = sbn.getNotification();
int uid = sbn.getUid();
-
if (!postedNotification.isMediaNotification()) {
return;
}
@@ -3138,11 +3196,9 @@
mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
mediaSessionRecord.getForegroundServiceDelegationOptions();
- if (mediaSessionRecord.isLinkedToNotification(postedNotification)
- && foregroundServiceDelegationOptions != null) {
- mActivityManagerInternal.startForegroundServiceDelegate(
- foregroundServiceDelegationOptions,
- /* connection= */ null);
+ if (foregroundServiceDelegationOptions != null
+ && mediaSessionRecord.isLinkedToNotification(postedNotification)) {
+ startFgsDelegate(foregroundServiceDelegationOptions);
return;
}
}
@@ -3173,21 +3229,7 @@
return;
}
- boolean shouldStopFgs = true;
- for (MediaSessionRecordImpl mediaSessionRecord :
- mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
- for (Notification mediaNotification :
- mMediaNotifications.getOrDefault(uid, Set.of())) {
- if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) {
- shouldStopFgs = false;
- }
- }
- }
- if (shouldStopFgs
- && notificationRecord.getForegroundServiceDelegationOptions() != null) {
- mActivityManagerInternal.stopForegroundServiceDelegate(
- notificationRecord.getForegroundServiceDelegationOptions());
- }
+ stopFgsIfNoSessionIsLinkedToNotification(notificationRecord);
}
}
diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java
index a563808..a20de31 100644
--- a/services/core/java/com/android/server/media/MediaShellCommand.java
+++ b/services/core/java/com/android/server/media/MediaShellCommand.java
@@ -92,6 +92,8 @@
runMonitor();
} else if (cmd.equals("volume")) {
runVolume();
+ } else if (cmd.equals("expire-temp-engaged-sessions")) {
+ expireTempEngagedSessions();
} else {
showError("Error: unknown command '" + cmd + "'");
return -1;
@@ -367,4 +369,8 @@
private void runVolume() throws Exception {
VolumeCtrl.run(this);
}
+
+ private void expireTempEngagedSessions() throws Exception {
+ mSessionService.expireTempEngagedSessions();
+ }
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 8df38a8..c105b9c 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -473,6 +473,10 @@
.setProviderId(mUniqueId)
.setSystemSession(true)
.addSelectedRoute(MediaRoute2Info.ROUTE_ID_DEFAULT)
+ .setTransferReason(newSessionInfo.getTransferReason())
+ .setTransferInitiator(
+ newSessionInfo.getTransferInitiatorUserHandle(),
+ newSessionInfo.getTransferInitiatorPackageName())
.build();
return true;
}
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index bd73cb6..1938642 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -23,9 +23,15 @@
import android.app.Notification;
import android.app.NotificationChannel;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.LoggingOnly;
import android.content.Context;
import android.media.AudioAttributes;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Slog;
+import com.android.internal.compat.IPlatformCompat;
/**
* Stores the latest notification channel information for this notification
@@ -34,14 +40,26 @@
private static final String TAG = "ChannelExtractor";
private static final boolean DBG = false;
+ /**
+ * Corrects audio attributes for notifications based on characteristics of the notifications.
+ */
+ @ChangeId
+ @LoggingOnly
+ static final long RESTRICT_AUDIO_ATTRIBUTES = 331793339L;
+
private RankingConfig mConfig;
private Context mContext;
+ private IPlatformCompat mPlatformCompat;
public void initialize(Context ctx, NotificationUsageStats usageStats) {
mContext = ctx;
if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
}
+ public void setCompatChangeLogger(IPlatformCompat platformCompat) {
+ mPlatformCompat = platformCompat;
+ }
+
public RankingReconsideration process(NotificationRecord record) {
if (record == null || record.getNotification() == null) {
if (DBG) Slog.d(TAG, "skipping empty notification");
@@ -80,6 +98,7 @@
}
if (updateAttributes) {
+ reportAudioAttributesChanged(record.getUid());
NotificationChannel clone = record.getChannel().copy();
clone.setSound(clone.getSound(), new AudioAttributes.Builder(attributes)
.setUsage(USAGE_NOTIFICATION)
@@ -91,6 +110,17 @@
return null;
}
+ private void reportAudioAttributesChanged(int uid) {
+ final long id = Binder.clearCallingIdentity();
+ try {
+ mPlatformCompat.reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, uid);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unexpected exception while reporting to changecompat", e);
+ } finally {
+ Binder.restoreCallingIdentity(id);
+ }
+ }
+
@Override
public void setConfig(RankingConfig config) {
mConfig = config;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerPrivate.java b/services/core/java/com/android/server/notification/NotificationManagerPrivate.java
index 2cc63eb..a3a91e2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerPrivate.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerPrivate.java
@@ -25,4 +25,6 @@
interface NotificationManagerPrivate {
@Nullable
NotificationRecord getNotificationByKey(String key);
+
+ void timeoutNotification(String key);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 956e10c..b48cad2 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -22,6 +22,7 @@
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR;
import static android.app.Flags.lifetimeExtensionRefactor;
import static android.app.Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
@@ -138,7 +139,6 @@
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.contentprotection.flags.Flags.rapidClearNotificationsByListenerAppOpEnabled;
-
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;
@@ -220,6 +220,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
+import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
@@ -237,6 +238,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.DeadObjectException;
import android.os.DeviceIdleManager;
import android.os.Environment;
import android.os.Handler;
@@ -304,7 +306,6 @@
import android.view.accessibility.AccessibilityManager;
import android.widget.RemoteViews;
import android.widget.Toast;
-
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -356,9 +357,7 @@
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.BackgroundActivityStartCallback;
import com.android.server.wm.WindowManagerInternal;
-
import libcore.io.IoUtils;
-
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParserException;
@@ -520,7 +519,7 @@
/**
* Apps that post custom toasts in the background will have those blocked. Apps can
* still post toasts created with
- * {@link android.widget.Toast#makeText(Context, CharSequence, int)} and its variants while
+ * {@link Toast#makeText(Context, CharSequence, int)} and its variants while
* in the background.
*/
@ChangeId
@@ -555,7 +554,7 @@
/**
* Rate limit showing toasts, on a per package basis.
*
- * It limits the number of {@link android.widget.Toast#show()} calls to prevent overburdening
+ * It limits the number of {@link Toast#show()} calls to prevent overburdening
* the user with too many toasts in a limited time. Any attempt to show more toasts than allowed
* in a certain time frame will result in the toast being discarded.
*/
@@ -579,9 +578,9 @@
static final long ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION = 264179692L;
/**
- * App calls to {@link android.app.NotificationManager#setInterruptionFilter} and
- * {@link android.app.NotificationManager#setNotificationPolicy} manage DND through the
- * creation and activation of an implicit {@link android.app.AutomaticZenRule}.
+ * App calls to {@link NotificationManager#setInterruptionFilter} and
+ * {@link NotificationManager#setNotificationPolicy} manage DND through the
+ * creation and activation of an implicit {@link AutomaticZenRule}.
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
@@ -623,6 +622,8 @@
private PowerManager mPowerManager;
private PostNotificationTrackerFactory mPostNotificationTrackerFactory;
+ private LockPatternUtils mLockUtils;
+
final IBinder mForegroundToken = new Binder();
@VisibleForTesting
WorkerHandler mHandler;
@@ -675,6 +676,10 @@
private static final int DB_VERSION = 1;
+
+ private static final String ADSERVICES_MODULE_PKG_NAME =
+ "com.android.adservices";
+
private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
private static final String ATTR_VERSION = "version";
@@ -701,13 +706,14 @@
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
- private static final IBinder ALLOWLIST_TOKEN = new Binder();
+ static final IBinder ALLOWLIST_TOKEN = new Binder();
protected RankingHandler mRankingHandler;
private long mLastOverRateLogTime;
private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
private NotificationHistoryManager mHistoryManager;
protected SnoozeHelper mSnoozeHelper;
+ private TimeToLiveHelper mTtlHelper;
private GroupHelper mGroupHelper;
private int mAutoGroupAtCount;
private boolean mIsTelevision;
@@ -733,6 +739,10 @@
// Broadcast intent receiver for notification permissions review-related intents
private ReviewNotificationPermissionsReceiver mReviewNotificationPermissionsReceiver;
+ private AppOpsManager.OnOpChangedListener mAppOpsListener;
+
+ private ModuleInfo mAdservicesModuleInfo;
+
static class Archive {
final SparseArray<Boolean> mEnabled;
final int mBufferSize;
@@ -778,7 +788,7 @@
public StatusBarNotification[] getArray(UserManager um, int count, boolean includeSnoozed) {
ArrayList<Integer> currentUsers = new ArrayList<>();
- currentUsers.add(UserHandle.USER_ALL);
+ currentUsers.add(USER_ALL);
Binder.withCleanCallingIdentity(() -> {
for (int user : um.getProfileIds(ActivityManager.getCurrentUser(), false)) {
currentUsers.add(user);
@@ -901,14 +911,14 @@
@VisibleForTesting
boolean isDNDMigrationDone(int userId) {
- return Settings.Secure.getIntForUser(getContext().getContentResolver(),
- Settings.Secure.DND_CONFIGS_MIGRATED, 0, userId) == 1;
+ return Secure.getIntForUser(getContext().getContentResolver(),
+ Secure.DND_CONFIGS_MIGRATED, 0, userId) == 1;
}
@VisibleForTesting
void setDNDMigrationDone(int userId) {
- Settings.Secure.putIntForUser(getContext().getContentResolver(),
- Settings.Secure.DND_CONFIGS_MIGRATED, 1, userId);
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.DND_CONFIGS_MIGRATED, 1, userId);
}
protected void migrateDefaultNAS() {
@@ -935,15 +945,15 @@
@VisibleForTesting
void setNASMigrationDone(int baseUserId) {
for (int profileId : mUm.getProfileIds(baseUserId, false)) {
- Settings.Secure.putIntForUser(getContext().getContentResolver(),
- Settings.Secure.NAS_SETTINGS_UPDATED, 1, profileId);
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NAS_SETTINGS_UPDATED, 1, profileId);
}
}
@VisibleForTesting
boolean isNASMigrationDone(int userId) {
- return (Settings.Secure.getIntForUser(getContext().getContentResolver(),
- Settings.Secure.NAS_SETTINGS_UPDATED, 0, userId) == 1);
+ return (Secure.getIntForUser(getContext().getContentResolver(),
+ Secure.NAS_SETTINGS_UPDATED, 0, userId) == 1);
}
boolean isProfileUser(UserInfo userInfo) {
@@ -1096,7 +1106,7 @@
mSnoozeHelper.readXml(parser, System.currentTimeMillis());
}
if (LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG.equals(parser.getName())) {
- if (forRestore && userId != UserHandle.USER_SYSTEM) {
+ if (forRestore && userId != USER_SYSTEM) {
continue;
}
mLockScreenAllowSecureNotifications = parser.getAttributeBoolean(null,
@@ -1140,7 +1150,7 @@
InputStream infile = null;
try {
infile = mPolicyFile.openRead();
- readPolicyXml(infile, false /*forRestore*/, UserHandle.USER_ALL);
+ readPolicyXml(infile, false /*forRestore*/, USER_ALL);
// We re-load the default dnd packages to allow the newly added and denined.
final boolean isWatch = mPackageManagerClient.hasSystemFeature(
@@ -1190,7 +1200,7 @@
}
try {
- writePolicyXml(stream, false /*forBackup*/, UserHandle.USER_ALL);
+ writePolicyXml(stream, false /*forBackup*/, USER_ALL);
mPolicyFile.finishWrite(stream);
} catch (IOException e) {
Slog.w(TAG, "Failed to save policy file, restoring backup", e);
@@ -1219,7 +1229,7 @@
mAssistants.writeXml(out, forBackup, userId);
mSnoozeHelper.writeXml(out);
mConditionProviders.writeXml(out, forBackup, userId);
- if (!forBackup || userId == UserHandle.USER_SYSTEM) {
+ if (!forBackup || userId == USER_SYSTEM) {
writeSecureNotificationsPolicy(out);
}
out.endTag(null, TAG_NOTIFICATION_POLICY);
@@ -1272,7 +1282,7 @@
StatusBarNotification sbn = r.getSbn();
cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
- sbn.getId(), Notification.FLAG_AUTO_CANCEL,
+ sbn.getId(), FLAG_AUTO_CANCEL,
FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_BUBBLE,
false, r.getUserId(), REASON_CLICK, nv.rank, nv.count, null);
nv.recycle();
@@ -1760,9 +1770,50 @@
};
- NotificationManagerPrivate mNotificationManagerPrivate = key -> {
- synchronized (mNotificationLock) {
- return mNotificationsByKey.get(key);
+ NotificationManagerPrivate mNotificationManagerPrivate = new NotificationManagerPrivate() {
+ @Nullable
+ @Override
+ public NotificationRecord getNotificationByKey(String key) {
+ synchronized (mNotificationLock) {
+ return mNotificationsByKey.get(key);
+ }
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_ALL_NOTIFS_NEED_TTL)
+ public void timeoutNotification(String key) {
+ boolean foundNotification = false;
+ int uid = 0;
+ int pid = 0;
+ String packageName = null;
+ String tag = null;
+ int id = 0;
+ int userId = 0;
+
+ synchronized (mNotificationLock) {
+ NotificationRecord record = findNotificationByKeyLocked(key);
+ if (record != null) {
+ foundNotification = true;
+ uid = record.getUid();
+ pid = record.getSbn().getInitialPid();
+ packageName = record.getSbn().getPackageName();
+ tag = record.getSbn().getTag();
+ id = record.getSbn().getId();
+ userId = record.getUserId();
+ }
+ }
+ if (foundNotification) {
+ if (lifetimeExtensionRefactor()) {
+ cancelNotification(uid, pid, packageName, tag, id, 0,
+ FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB
+ | FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY,
+ true, userId, REASON_TIMEOUT, null);
+ } else {
+ cancelNotification(uid, pid, packageName, tag, id, 0,
+ FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB,
+ true, userId, REASON_TIMEOUT, null);
+ }
+ }
}
};
@@ -1892,7 +1943,7 @@
|| action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED)
|| action.equals(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED)) {
int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_ALL);
+ USER_ALL);
String pkgList[] = null;
int uidList[] = null;
boolean removingPackage = queryRemove &&
@@ -1941,7 +1992,7 @@
try {
final int enabled = mPackageManager.getApplicationEnabledSetting(
pkgName,
- changeUserId != UserHandle.USER_ALL ? changeUserId :
+ changeUserId != USER_ALL ? changeUserId :
USER_SYSTEM);
if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|| enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
@@ -2054,22 +2105,22 @@
private final class SettingsObserver extends ContentObserver {
private final Uri NOTIFICATION_BADGING_URI
- = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
+ = Secure.getUriFor(Secure.NOTIFICATION_BADGING);
private final Uri NOTIFICATION_BUBBLES_URI
- = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BUBBLES);
+ = Secure.getUriFor(Secure.NOTIFICATION_BUBBLES);
private final Uri NOTIFICATION_RATE_LIMIT_URI
= Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
private final Uri NOTIFICATION_HISTORY_ENABLED
- = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_HISTORY_ENABLED);
+ = Secure.getUriFor(Secure.NOTIFICATION_HISTORY_ENABLED);
private final Uri NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI
= Settings.Global.getUriFor(Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS);
private final Uri LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
- = Settings.Secure.getUriFor(
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ = Secure.getUriFor(
+ Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
private final Uri LOCK_SCREEN_SHOW_NOTIFICATIONS
- = Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+ = Secure.getUriFor(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
private final Uri SHOW_NOTIFICATION_SNOOZE
- = Settings.Secure.getUriFor(Settings.Secure.SHOW_NOTIFICATION_SNOOZE);
+ = Secure.getUriFor(Secure.SHOW_NOTIFICATION_SNOOZE);
SettingsObserver(Handler handler) {
super(handler);
@@ -2078,27 +2129,31 @@
void observe() {
ContentResolver resolver = getContext().getContentResolver();
resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
- false, this, UserHandle.USER_ALL);
+ false, this, USER_ALL);
resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
- false, this, UserHandle.USER_ALL);
+ false, this, USER_ALL);
resolver.registerContentObserver(NOTIFICATION_BUBBLES_URI,
- false, this, UserHandle.USER_ALL);
+ false, this, USER_ALL);
resolver.registerContentObserver(NOTIFICATION_HISTORY_ENABLED,
- false, this, UserHandle.USER_ALL);
+ false, this, USER_ALL);
resolver.registerContentObserver(NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI,
- false, this, UserHandle.USER_ALL);
+ false, this, USER_ALL);
resolver.registerContentObserver(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
- false, this, UserHandle.USER_ALL);
+ false, this, USER_ALL);
resolver.registerContentObserver(LOCK_SCREEN_SHOW_NOTIFICATIONS,
- false, this, UserHandle.USER_ALL);
+ false, this, USER_ALL);
resolver.registerContentObserver(SHOW_NOTIFICATION_SNOOZE,
- false, this, UserHandle.USER_ALL);
+ false, this, USER_ALL);
update(null);
}
+ void destroy() {
+ getContext().getContentResolver().unregisterContentObserver(this);
+ }
+
@Override public void onChange(boolean selfChange, Uri uri, int userId) {
update(uri);
}
@@ -2130,7 +2185,7 @@
mPreferencesHelper.updateLockScreenShowNotifications();
}
if (SHOW_NOTIFICATION_SNOOZE.equals(uri)) {
- final boolean snoozeEnabled = Settings.Secure.getIntForUser(resolver,
+ final boolean snoozeEnabled = Secure.getIntForUser(resolver,
Secure.SHOW_NOTIFICATION_SNOOZE, 0, UserHandle.USER_CURRENT)
!= 0;
if (!snoozeEnabled) {
@@ -2143,8 +2198,8 @@
ContentResolver resolver = getContext().getContentResolver();
if (uri == null || NOTIFICATION_HISTORY_ENABLED.equals(uri)) {
mArchive.updateHistoryEnabled(userId,
- Settings.Secure.getIntForUser(resolver,
- Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0,
+ Secure.getIntForUser(resolver,
+ Secure.NOTIFICATION_HISTORY_ENABLED, 0,
userId) == 1);
// note: this setting is also handled in NotificationHistoryManager
}
@@ -2228,6 +2283,11 @@
}
@VisibleForTesting
+ void setLockPatternUtils(LockPatternUtils lockUtils) {
+ mLockUtils = lockUtils;
+ }
+
+ @VisibleForTesting
ShortcutHelper getShortcutHelper() {
return mShortcutHelper;
}
@@ -2399,7 +2459,7 @@
getContext().sendBroadcastAsUser(
new Intent(ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
- UserHandle.ALL, permission.MANAGE_NOTIFICATIONS);
+ UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
synchronized (mNotificationLock) {
updateInterruptionFilterLocked();
}
@@ -2455,15 +2515,14 @@
mAppOps,
mUserProfiles,
mShowReviewPermissionsNotification);
- mRankingHelper = new RankingHelper(getContext(),
- mRankingHandler,
- mPreferencesHelper,
- mZenModeHelper,
- mUsageStats,
- extractorNames);
+ mRankingHelper = new RankingHelper(getContext(), mRankingHandler, mPreferencesHelper,
+ mZenModeHelper, mUsageStats, extractorNames, mPlatformCompat);
mSnoozeHelper = snoozeHelper;
mGroupHelper = groupHelper;
mHistoryManager = historyManager;
+ if (Flags.allNotifsNeedTtl()) {
+ mTtlHelper = new TimeToLiveHelper(mNotificationManagerPrivate, getContext());
+ }
// This is a ManagedServices object that keeps track of the listeners.
mListeners = notificationListeners;
@@ -2550,10 +2609,12 @@
getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
null);
- IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
- timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
- getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter,
- Context.RECEIVER_EXPORTED_UNAUDITED);
+ if (!Flags.allNotifsNeedTtl()) {
+ IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
+ timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
+ getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
+ }
IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter);
@@ -2566,15 +2627,16 @@
ReviewNotificationPermissionsReceiver.getFilter(),
Context.RECEIVER_NOT_EXPORTED);
- mAppOps.startWatchingMode(AppOpsManager.OP_POST_NOTIFICATION, null,
- new AppOpsManager.OnOpChangedInternalListener() {
- @Override
- public void onOpChanged(@NonNull String op, @NonNull String packageName,
- int userId) {
- mHandler.post(
- () -> handleNotificationPermissionChange(packageName, userId));
- }
- });
+ mAppOpsListener = new AppOpsManager.OnOpChangedInternalListener() {
+ @Override
+ public void onOpChanged(@NonNull String op, @NonNull String packageName,
+ int userId) {
+ mHandler.post(
+ () -> handleNotificationPermissionChange(packageName, userId));
+ }
+ };
+
+ mAppOps.startWatchingMode(AppOpsManager.OP_POST_NOTIFICATION, null, mAppOpsListener);
}
/**
@@ -2583,10 +2645,26 @@
public void onDestroy() {
getContext().unregisterReceiver(mIntentReceiver);
getContext().unregisterReceiver(mPackageIntentReceiver);
- getContext().unregisterReceiver(mNotificationTimeoutReceiver);
+ if (Flags.allNotifsNeedTtl()) {
+ mTtlHelper.destroy();
+ } else {
+ getContext().unregisterReceiver(mNotificationTimeoutReceiver);
+ }
getContext().unregisterReceiver(mRestoreReceiver);
getContext().unregisterReceiver(mLocaleChangeReceiver);
+ mSettingsObserver.destroy();
+ mRoleObserver.destroy();
+ if (mShortcutHelper != null) {
+ mShortcutHelper.destroy();
+ }
+ mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_PREFERENCES);
+ mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
+ mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
+ mStatsManager.clearPullAtomCallback(DND_MODE_RULE);
+ mAppOps.stopWatchingMode(mAppOpsListener);
+ mAlarmManager.cancelAll();
+
if (mDeviceConfigChangedListener != null) {
DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
}
@@ -2841,7 +2919,10 @@
bubbsExtractor.setShortcutHelper(mShortcutHelper);
}
registerNotificationPreferencesPullers();
- new LockPatternUtils(getContext()).registerStrongAuthTracker(mStrongAuthTracker);
+ if (mLockUtils == null) {
+ mLockUtils = new LockPatternUtils(getContext());
+ }
+ mLockUtils.registerStrongAuthTracker(mStrongAuthTracker);
mAttentionHelper.onSystemReady();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
// This observer will force an update when observe is called, causing us to
@@ -2860,6 +2941,15 @@
mZenModeHelper.setDeviceEffectsApplier(
new DefaultDeviceEffectsApplier(getContext()));
}
+ List<ModuleInfo> moduleInfoList =
+ mPackageManagerClient.getInstalledModules(
+ PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
+ // Cache adservices module info
+ for (ModuleInfo mi : moduleInfoList) {
+ if (Objects.equals(mi.getApexModuleName(), ADSERVICES_MODULE_PKG_NAME)) {
+ mAdservicesModuleInfo = mi;
+ }
+ }
} else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mSnoozeHelper.scheduleRepostsForPersistedNotifications(System.currentTimeMillis());
} else if (phase == SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY) {
@@ -2951,7 +3041,7 @@
void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
boolean fromListener) {
- if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
+ if (channel.getImportance() == IMPORTANCE_NONE) {
// cancel
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0,
UserHandle.getUserId(uid), REASON_CHANNEL_BANNED
@@ -3216,14 +3306,14 @@
| SUPPRESSED_EFFECT_SCREEN_OFF);
// set the deprecated effects according to the new more specific effects
- if ((newSuppressedVisualEffects & Policy.SUPPRESSED_EFFECT_PEEK) != 0) {
+ if ((newSuppressedVisualEffects & SUPPRESSED_EFFECT_PEEK) != 0) {
newSuppressedVisualEffects |= SUPPRESSED_EFFECT_SCREEN_ON;
}
- if ((newSuppressedVisualEffects & Policy.SUPPRESSED_EFFECT_LIGHTS) != 0
+ if ((newSuppressedVisualEffects & SUPPRESSED_EFFECT_LIGHTS) != 0
&& (newSuppressedVisualEffects
- & Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) != 0
+ & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) != 0
&& (newSuppressedVisualEffects
- & Policy.SUPPRESSED_EFFECT_AMBIENT) != 0) {
+ & SUPPRESSED_EFFECT_AMBIENT) != 0) {
newSuppressedVisualEffects |= SUPPRESSED_EFFECT_SCREEN_OFF;
}
} else {
@@ -3291,7 +3381,7 @@
if (n.extras != null) {
title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
if (title == null) {
- title = n.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
+ title = n.extras.getCharSequence(EXTRA_TITLE_BIG);
}
}
return title == null ? getContext().getResources().getString(
@@ -3310,9 +3400,9 @@
if (nb.getStyle() instanceof Notification.BigTextStyle) {
text = ((Notification.BigTextStyle) nb.getStyle()).getBigText();
- } else if (nb.getStyle() instanceof Notification.MessagingStyle) {
- Notification.MessagingStyle ms = (Notification.MessagingStyle) nb.getStyle();
- final List<Notification.MessagingStyle.Message> messages = ms.getMessages();
+ } else if (nb.getStyle() instanceof MessagingStyle) {
+ MessagingStyle ms = (MessagingStyle) nb.getStyle();
+ final List<MessagingStyle.Message> messages = ms.getMessages();
if (messages != null && messages.size() > 0) {
text = messages.get(messages.size() - 1).getText();
}
@@ -3363,7 +3453,7 @@
}
private int getRealUserId(int userId) {
- return userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId;
+ return userId == USER_ALL ? USER_SYSTEM : userId;
}
private ToastRecord getToastRecord(int uid, int pid, String packageName, boolean isSystemToast,
@@ -3399,21 +3489,21 @@
// ============================================================================
@Override
- public void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration,
+ public boolean enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration,
boolean isUiContext, int displayId,
@Nullable ITransientNotificationCallback textCallback) {
- enqueueToast(pkg, token, text, /* callback= */ null, duration, isUiContext, displayId,
- textCallback);
+ return enqueueToast(pkg, token, text, /* callback= */ null, duration, isUiContext,
+ displayId, textCallback);
}
@Override
- public void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
+ public boolean enqueueToast(String pkg, IBinder token, ITransientNotification callback,
int duration, boolean isUiContext, int displayId) {
- enqueueToast(pkg, token, /* text= */ null, callback, duration, isUiContext, displayId,
- /* textCallback= */ null);
+ return enqueueToast(pkg, token, /* text= */ null, callback, duration, isUiContext,
+ displayId, /* textCallback= */ null);
}
- private void enqueueToast(String pkg, IBinder token, @Nullable CharSequence text,
+ private boolean enqueueToast(String pkg, IBinder token, @Nullable CharSequence text,
@Nullable ITransientNotification callback, int duration, boolean isUiContext,
int displayId, @Nullable ITransientNotificationCallback textCallback) {
if (DBG) {
@@ -3425,7 +3515,7 @@
|| (text != null && callback != null) || token == null) {
Slog.e(TAG, "Not enqueuing toast. pkg=" + pkg + " text=" + text + " callback="
+ " token=" + token);
- return;
+ return false;
}
final int callingUid = Binder.getCallingUid();
@@ -3451,7 +3541,7 @@
boolean isAppRenderedToast = (callback != null);
if (!checkCanEnqueueToast(pkg, callingUid, displayId, isAppRenderedToast,
isSystemToast)) {
- return;
+ return false;
}
synchronized (mToastQueue) {
@@ -3477,7 +3567,7 @@
if (count >= MAX_PACKAGE_TOASTS) {
Slog.e(TAG, "Package has already queued " + count
+ " toasts. Not showing more. Package=" + pkg);
- return;
+ return false;
}
}
}
@@ -3513,6 +3603,7 @@
Binder.restoreCallingIdentity(callingId);
}
}
+ return true;
}
@GuardedBy("mToastQueue")
@@ -3597,8 +3688,8 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING)
@Override
+ @EnforcePermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING)
public void setToastRateLimitingEnabled(boolean enable) {
super.setToastRateLimitingEnabled_enforcePermission();
@@ -4094,8 +4185,8 @@
public void createConversationNotificationChannelForPackage(String pkg, int uid,
NotificationChannel parentChannel, String conversationId) {
enforceSystemOrSystemUI("only system can call this");
- Preconditions.checkNotNull(parentChannel);
- Preconditions.checkNotNull(conversationId);
+ checkNotNull(parentChannel);
+ checkNotNull(conversationId);
String parentId = parentChannel.getId();
NotificationChannel conversationChannel = parentChannel;
conversationChannel.setId(String.format(
@@ -4523,7 +4614,6 @@
return getActiveNotificationsWithAttribution(callingPkg, null);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
/**
* System-only API for getting a list of current (i.e. not cleared) notifications.
*
@@ -4531,6 +4621,7 @@
* @returns A list of all the notifications, in natural order.
*/
@Override
+ @EnforcePermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
public StatusBarNotification[] getActiveNotificationsWithAttribution(String callingPkg,
String callingAttributionTag) {
// enforce() will ensure the calling uid has the correct permission
@@ -4540,7 +4631,7 @@
int uid = Binder.getCallingUid();
ArrayList<Integer> currentUsers = new ArrayList<>();
- currentUsers.add(UserHandle.USER_ALL);
+ currentUsers.add(USER_ALL);
Binder.withCleanCallingIdentity(() -> {
for (int user : mUm.getProfileIds(ActivityManager.getCurrentUser(), false)) {
currentUsers.add(user);
@@ -4548,9 +4639,9 @@
});
// noteOp will check to make sure the callingPkg matches the uid
- if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
- callingAttributionTag, null)
- == MODE_ALLOWED) {
+ int mode = mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
+ callingAttributionTag, null);
+ if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
synchronized (mNotificationLock) {
final int N = mNotificationList.size();
for (int i = 0; i < N; i++) {
@@ -4626,7 +4717,7 @@
// Remove background token before returning notification to untrusted app, this
// ensures the app isn't able to perform background operations that are
// associated with notification interactions.
- notification.clearAllowlistToken();
+ notification.overrideAllowlistToken(null);
return new StatusBarNotification(
sbn.getPackageName(),
sbn.getOpPkg(),
@@ -4650,12 +4741,12 @@
includeSnoozed);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
/**
* System-only API for getting a list of recent (cleared, no longer shown) notifications.
*/
@Override
@RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
+ @EnforcePermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
public StatusBarNotification[] getHistoricalNotificationsWithAttribution(String callingPkg,
String callingAttributionTag, int count, boolean includeSnoozed) {
// enforce() will ensure the calling uid has the correct permission
@@ -4665,9 +4756,9 @@
int uid = Binder.getCallingUid();
// noteOp will check to make sure the callingPkg matches the uid
- if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
- callingAttributionTag, null)
- == MODE_ALLOWED) {
+ int mode = mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
+ callingAttributionTag, null);
+ if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
synchronized (mArchive) {
tmp = mArchive.getArray(mUm, count, includeSnoozed);
}
@@ -4675,7 +4766,6 @@
return tmp;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
/**
* System-only API for getting a list of historical notifications. May contain multiple days
* of notifications.
@@ -4683,6 +4773,7 @@
@Override
@WorkerThread
@RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
+ @EnforcePermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
public NotificationHistory getNotificationHistory(String callingPkg,
String callingAttributionTag) {
// enforce() will ensure the calling uid has the correct permission
@@ -4690,9 +4781,9 @@
int uid = Binder.getCallingUid();
// noteOp will check to make sure the callingPkg matches the uid
- if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
- callingAttributionTag, null)
- == MODE_ALLOWED) {
+ int mode = mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
+ callingAttributionTag, null);
+ if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
IntArray currentUserIds = mUserProfiles.getCurrentProfileIds();
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryReadHistory");
try {
@@ -4796,7 +4887,7 @@
* Register a listener binder directly with the notification manager.
*
* Only works with system callers. Apps should extend
- * {@link android.service.notification.NotificationListenerService}.
+ * {@link NotificationListenerService}.
*/
@Override
public void registerListener(final INotificationListener listener,
@@ -4853,7 +4944,7 @@
NotificationRecord r = mNotificationsByKey.get(keys[i]);
if (r == null) continue;
final int userId = r.getSbn().getUserId();
- if (userId != info.userid && userId != UserHandle.USER_ALL &&
+ if (userId != info.userid && userId != USER_ALL &&
!mUserProfiles.isCurrentProfile(userId)) {
continue;
}
@@ -4970,7 +5061,7 @@
NotificationRecord r = mNotificationsByKey.get(keys[i]);
if (r == null) continue;
final int userId = r.getSbn().getUserId();
- if (userId != info.userid && userId != UserHandle.USER_ALL
+ if (userId != info.userid && userId != USER_ALL
&& !mUserProfiles.isCurrentProfile(userId)) {
continue;
}
@@ -5637,7 +5728,7 @@
}
private void enforcePolicyAccess(int uid, String method) {
- if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
+ if (PERMISSION_GRANTED == getContext().checkCallingPermission(
android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
return;
}
@@ -5668,7 +5759,7 @@
}
private void enforcePolicyAccess(String pkg, String method) {
- if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
+ if (PERMISSION_GRANTED == getContext().checkCallingPermission(
android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
return;
}
@@ -5689,7 +5780,7 @@
try {
uid = getContext().getPackageManager().getPackageUidAsUser(pkg,
UserHandle.getCallingUserId());
- if (PackageManager.PERMISSION_GRANTED == checkComponentPermission(
+ if (PERMISSION_GRANTED == checkComponentPermission(
android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
-1, true)) {
return true;
@@ -5884,7 +5975,7 @@
/**
* Sets the notification policy. Apps that target API levels below
- * {@link android.os.Build.VERSION_CODES#P} cannot change user-designated values to
+ * {@link Build.VERSION_CODES#P} cannot change user-designated values to
* allow or disallow {@link Policy#PRIORITY_CATEGORY_ALARMS},
* {@link Policy#PRIORITY_CATEGORY_SYSTEM} and
* {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd
@@ -6252,7 +6343,7 @@
@Override
public void setPrivateNotificationsAllowed(boolean allow) {
- if (PackageManager.PERMISSION_GRANTED
+ if (PERMISSION_GRANTED
!= getContext().checkCallingPermission(CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
throw new SecurityException(
"Requires CONTROL_KEYGUARD_SECURE_NOTIFICATIONS permission");
@@ -6273,7 +6364,7 @@
@Override
public boolean getPrivateNotificationsAllowed() {
- if (PackageManager.PERMISSION_GRANTED
+ if (PERMISSION_GRANTED
!= getContext().checkCallingPermission(CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
throw new SecurityException(
"Requires CONTROL_KEYGUARD_SECURE_NOTIFICATIONS permission");
@@ -6561,9 +6652,9 @@
// Add summary
final ApplicationInfo appInfo =
adjustedSbn.getNotification().extras.getParcelable(
- Notification.EXTRA_BUILDER_APPLICATION_INFO, ApplicationInfo.class);
+ EXTRA_BUILDER_APPLICATION_INFO, ApplicationInfo.class);
final Bundle extras = new Bundle();
- extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
+ extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, appInfo);
final String channelId = notificationRecord.getChannel().getId();
final Notification summaryNotification =
@@ -6857,6 +6948,11 @@
if (!zenOnly) {
pw.println("\n Usage Stats:");
mUsageStats.dump(pw, " ", filter);
+
+ if (Flags.allNotifsNeedTtl()) {
+ pw.println("\n TimeToLive alarms:");
+ mTtlHelper.dump(pw, " ");
+ }
}
}
}
@@ -7213,6 +7309,17 @@
+ " trying to post for invalid pkg " + pkg + " in user " + incomingUserId);
}
+ if (android.app.Flags.secureAllowlistToken()) {
+ IBinder allowlistToken = notification.getAllowlistToken();
+ if (allowlistToken != null && allowlistToken != ALLOWLIST_TOKEN) {
+ throw new SecurityException(
+ "Unexpected allowlist token received from " + callingUid);
+ }
+ // allowlistToken is populated by unparceling, so it can be null if the notification was
+ // posted from inside system_server. Ensure it's the expected value.
+ notification.overrideAllowlistToken(ALLOWLIST_TOKEN);
+ }
+
checkRestrictedCategories(notification);
// Notifications passed to setForegroundService() have FLAG_FOREGROUND_SERVICE,
@@ -7442,7 +7549,7 @@
throws NameNotFoundException, RemoteException {
final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
+ (userId == USER_ALL) ? USER_SYSTEM : userId);
Notification.addFieldsFromContext(ai, notification);
if (notification.isForegroundService() && fgsPolicy == NOT_FOREGROUND_SERVICE) {
@@ -7557,7 +7664,7 @@
// Enforce NO_CLEAR flag on MediaStyle notification for apps with targetSdk >= V.
if (CompatChanges.isChangeEnabled(ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION,
notificationUid)) {
- notification.flags |= Notification.FLAG_NO_CLEAR;
+ notification.flags |= FLAG_NO_CLEAR;
}
}
@@ -7592,13 +7699,27 @@
private boolean canBeNonDismissible(ApplicationInfo ai, Notification notification) {
return notification.isMediaNotification() || isEnterpriseExempted(ai)
|| notification.isStyle(Notification.CallStyle.class)
- || isDefaultSearchSelectorPackage(ai.packageName);
+ || isDefaultSearchSelectorPackage(ai.packageName)
+ || isDefaultAdservicesPackage(ai.packageName);
}
private boolean isDefaultSearchSelectorPackage(String pkg) {
return Objects.equals(mDefaultSearchSelectorPkg, pkg);
}
+ private boolean isDefaultAdservicesPackage(String pkg) {
+ if (mAdservicesModuleInfo == null) {
+ return false;
+ }
+ // Handles the special package structure for mainline modules
+ for (String apkName : mAdservicesModuleInfo.getApkInApexPackageNames()) {
+ if (Objects.equals(apkName, pkg)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean isEnterpriseExempted(ApplicationInfo ai) {
// Check if the app is an organization admin app
// TODO(b/234609037): Replace with new DPM APIs to check if organization admin
@@ -7609,7 +7730,7 @@
// Check if an app has been given system exemption
return mSystemExemptFromDismissal && mAppOps.checkOpNoThrow(
AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, ai.uid,
- ai.packageName) == AppOpsManager.MODE_ALLOWED;
+ ai.packageName) == MODE_ALLOWED;
}
private boolean checkUseFullScreenIntentPermission(@NonNull AttributionSource attributionSource,
@@ -7714,7 +7835,7 @@
// Enqueue will trigger resort & flag is updated that way.
r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
mHandler.post(
- new NotificationManagerService.EnqueueNotificationRunnable(
+ new EnqueueNotificationRunnable(
r.getUser().getIdentifier(), r, isAppForeground,
mPostNotificationTrackerFactory.newTracker(null)));
}
@@ -7736,7 +7857,7 @@
@VisibleForTesting
int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId) {
- if (userId == UserHandle.USER_ALL) {
+ if (userId == USER_ALL) {
userId = USER_SYSTEM;
}
// posted from app A on behalf of app A
@@ -7995,7 +8116,7 @@
final String pkg = r.getSbn().getPackageName();
final int callingUid = r.getSbn().getUid();
return mPreferencesHelper.isGroupBlocked(pkg, callingUid, r.getChannel().getGroup())
- || r.getImportance() == NotificationManager.IMPORTANCE_NONE;
+ || r.getImportance() == IMPORTANCE_NONE;
}
protected class SnoozeNotificationRunnable implements Runnable {
@@ -8334,7 +8455,11 @@
}
mEnqueuedNotifications.add(r);
- scheduleTimeoutLocked(r);
+ if (Flags.allNotifsNeedTtl()) {
+ mTtlHelper.scheduleTimeoutLocked(r, SystemClock.elapsedRealtime());
+ } else {
+ scheduleTimeoutLocked(r);
+ }
final StatusBarNotification n = r.getSbn();
if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
@@ -8554,7 +8679,7 @@
Slog.e(TAG, "Not posting notification without small icon: " + notification);
if (old != null && !old.isCanceled) {
mListeners.notifyRemovedLocked(r,
- NotificationListenerService.REASON_ERROR, r.getStats());
+ REASON_ERROR, r.getStats());
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -8922,7 +9047,7 @@
try {
isExemptFromRateLimiting = mPackageManager.checkPermission(
android.Manifest.permission.UNLIMITED_TOASTS, pkg, userId)
- == PackageManager.PERMISSION_GRANTED;
+ == PERMISSION_GRANTED;
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect with package manager");
}
@@ -9447,7 +9572,11 @@
int rank, int count, boolean wasPosted, String listenerName,
@ElapsedRealtimeLong long cancellationElapsedTimeMs) {
final String canceledKey = r.getKey();
- cancelScheduledTimeoutLocked(r);
+ if (Flags.allNotifsNeedTtl()) {
+ mTtlHelper.cancelScheduledTimeoutLocked(r);
+ } else {
+ cancelScheduledTimeoutLocked(r);
+ }
// Record caller.
recordCallerLocked(r);
@@ -9624,7 +9753,7 @@
// Uri, not when removing an individual listener.
revokeUriPermission(permissionOwner, uri,
UserHandle.getUserId(oldRecord.getUid()),
- null, UserHandle.USER_ALL);
+ null, USER_ALL);
}
}
}
@@ -9722,9 +9851,9 @@
} else {
return
// looking for USER_ALL notifications? match everything
- userId == UserHandle.USER_ALL
+ userId == USER_ALL
// a notification sent to USER_ALL matches any query
- || r.getUserId() == UserHandle.USER_ALL
+ || r.getUserId() == USER_ALL
// an exact user match
|| r.getUserId() == userId;
}
@@ -9803,7 +9932,7 @@
continue;
}
// Don't remove notifications to all, if there's no package name specified
- if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
+ if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == USER_ALL) {
continue;
}
if (!flagChecker.apply(r.getFlags())) {
@@ -10301,7 +10430,7 @@
return false;
}
- if (userId == UserHandle.USER_ALL) {
+ if (userId == USER_ALL) {
userId = USER_SYSTEM;
}
@@ -10524,7 +10653,7 @@
if (requiredPermission != null) {
try {
if (mPackageManager.checkPermission(requiredPermission, pkg, userId)
- != PackageManager.PERMISSION_GRANTED) {
+ != PERMISSION_GRANTED) {
canUseManagedServices = false;
}
} catch (RemoteException e) {
@@ -10650,7 +10779,7 @@
c.caption = "notification assistant";
c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
c.xmlTag = TAG_ENABLED_NOTIFICATION_ASSISTANTS;
- c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
+ c.secureSettingName = Secure.ENABLED_NOTIFICATION_ASSISTANT;
c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
c.clientLabel = R.string.notification_ranker_binding_label;
@@ -11246,7 +11375,7 @@
c.caption = "notification listener";
c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
c.xmlTag = TAG_ENABLED_NOTIFICATION_LISTENERS;
- c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
+ c.secureSettingName = Secure.ENABLED_NOTIFICATION_LISTENERS;
c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
c.clientLabel = R.string.notification_listener_binding_label;
@@ -11654,7 +11783,7 @@
// Managed Services.
if (info.isSystemUi() && old != null && old.getNotification() != null
&& (old.getNotification().flags
- & Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0) {
+ & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0) {
final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
listenerCalls.add(() -> notifyPosted(info, oldSbn, update));
break;
@@ -11683,8 +11812,8 @@
continue;
}
// Grant access before listener is notified
- final int targetUserId = (info.userid == UserHandle.USER_ALL)
- ? UserHandle.USER_SYSTEM : info.userid;
+ final int targetUserId = (info.userid == USER_ALL)
+ ? USER_SYSTEM : info.userid;
updateUriPermissions(r, old, info.component.getPackageName(), targetUserId);
mPackageManagerInternal.grantImplicitAccess(
@@ -11833,8 +11962,8 @@
continue;
}
// Grant or revoke access synchronously
- final int targetUserId = (info.userid == UserHandle.USER_ALL)
- ? UserHandle.USER_SYSTEM : info.userid;
+ final int targetUserId = (info.userid == USER_ALL)
+ ? USER_SYSTEM : info.userid;
if (grant) {
// Grant permissions by passing arguments as if the notification is new.
updateUriPermissions(/* newRecord */ r, /* oldRecord */ null,
@@ -11906,7 +12035,7 @@
}
// Revoke access after all listeners have been updated
- mHandler.post(() -> updateUriPermissions(null, r, null, UserHandle.USER_SYSTEM));
+ mHandler.post(() -> updateUriPermissions(null, r, null, USER_SYSTEM));
}
/**
@@ -12066,7 +12195,7 @@
StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
listener.onNotificationPosted(sbnHolder, rankingUpdate);
- } catch (android.os.DeadObjectException ex) {
+ } catch (DeadObjectException ex) {
Slog.wtf(TAG, "unable to notify listener (posted): " + info, ex);
} catch (RemoteException ex) {
Slog.e(TAG, "unable to notify listener (posted): " + info, ex);
@@ -12089,7 +12218,7 @@
reason = REASON_LISTENER_CANCEL;
}
listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason);
- } catch (android.os.DeadObjectException ex) {
+ } catch (DeadObjectException ex) {
Slog.wtf(TAG, "unable to notify listener (removed): " + info, ex);
} catch (RemoteException ex) {
Slog.e(TAG, "unable to notify listener (removed): " + info, ex);
@@ -12101,7 +12230,7 @@
final INotificationListener listener = (INotificationListener) info.service;
try {
listener.onNotificationRankingUpdate(rankingUpdate);
- } catch (android.os.DeadObjectException ex) {
+ } catch (DeadObjectException ex) {
Slog.wtf(TAG, "unable to notify listener (ranking update): " + info, ex);
} catch (RemoteException ex) {
Slog.e(TAG, "unable to notify listener (ranking update): " + info, ex);
@@ -12320,6 +12449,10 @@
mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.ALL);
}
+ void destroy() {
+ mRm.removeOnRoleHoldersChangedListenerAsUser(this, UserHandle.ALL);
+ }
+
@VisibleForTesting
public boolean isApprovedPackageForRoleForUser(String role, String pkg, int userId) {
return mNonBlockableDefaultApps.get(role).get(userId).contains(pkg);
@@ -12608,7 +12741,7 @@
.setContentIntent(PendingIntent.getActivity(getContext(), 0, tapIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
.setStyle(new Notification.BigTextStyle())
- .setFlag(Notification.FLAG_NO_CLEAR, true)
+ .setFlag(FLAG_NO_CLEAR, true)
.setAutoCancel(true)
.addAction(remindMe)
.addAction(dismiss)
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 97d2620..c69bead 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -79,6 +79,7 @@
import java.io.PrintWriter;
import java.lang.reflect.Array;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -600,8 +601,7 @@
pw.println(prefix + "headsUpContentView="
+ formatRemoteViews(notification.headsUpContentView));
pw.println(prefix + String.format("color=0x%08x", notification.color));
- pw.println(prefix + "timeout="
- + TimeUtils.formatForLogging(notification.getTimeoutAfter()));
+ pw.println(prefix + "timeout=" + Duration.ofMillis(notification.getTimeoutAfter()));
if (notification.actions != null && notification.actions.length > 0) {
pw.println(prefix + "actions={");
final int N = notification.actions.length;
diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
index 24c1d59..f0358d1 100644
--- a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
@@ -17,6 +17,7 @@
package com.android.server.notification;
import android.content.Context;
+import com.android.internal.compat.IPlatformCompat;
/**
* Extracts signals that will be useful to the {@link NotificationComparator} and caches them
@@ -52,4 +53,6 @@
* DND.
*/
void setZenHelper(ZenModeHelper helper);
+
+ default void setCompatChangeLogger(IPlatformCompat platformCompat){};
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 50ca984..461bd9c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1387,8 +1387,7 @@
public void updateFixedImportance(List<UserInfo> users) {
for (UserInfo user : users) {
List<PackageInfo> packages = mPm.getInstalledPackagesAsUser(
- PackageManager.PackageInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY),
- user.getUserHandle().getIdentifier());
+ 0, user.getUserHandle().getIdentifier());
for (PackageInfo pi : packages) {
boolean fixed = mPermissionHelper.isPermissionFixed(
pi.packageName, user.getUserHandle().getIdentifier());
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 68e0eaa..7756801 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -15,6 +15,9 @@
*/
package com.android.server.notification;
+import static android.app.Flags.restrictAudioAttributesAlarm;
+import static android.app.Flags.restrictAudioAttributesCall;
+import static android.app.Flags.restrictAudioAttributesMedia;
import static android.app.Flags.sortSectionByTime;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.text.TextUtils.formatSimple;
@@ -27,6 +30,7 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.compat.IPlatformCompat;
import com.android.tools.r8.keepanno.annotations.KeepItemKind;
import com.android.tools.r8.keepanno.annotations.KeepTarget;
import com.android.tools.r8.keepanno.annotations.UsesReflection;
@@ -56,7 +60,8 @@
methodName = "<init>")
})
public RankingHelper(Context context, RankingHandler rankingHandler, RankingConfig config,
- ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames) {
+ ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames,
+ IPlatformCompat platformCompat) {
mContext = context;
mRankingHandler = rankingHandler;
if (sortSectionByTime()) {
@@ -75,6 +80,10 @@
extractor.initialize(mContext, usageStats);
extractor.setConfig(config);
extractor.setZenHelper(zenHelper);
+ if (restrictAudioAttributesAlarm() || restrictAudioAttributesMedia()
+ || restrictAudioAttributesCall()) {
+ extractor.setCompatChangeLogger(platformCompat);
+ }
mSignalExtractors[i] = extractor;
} catch (ClassNotFoundException e) {
Slog.w(TAG, "Couldn't find extractor " + extractorNames[i] + ".", e);
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
index fc106b8..86dcecf 100644
--- a/services/core/java/com/android/server/notification/ShortcutHelper.java
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -287,4 +287,11 @@
}
}
}
+
+ void destroy() {
+ if (mLauncherAppsCallbackRegistered) {
+ mLauncherAppsService.unregisterCallback(mLauncherAppsCallback);
+ mLauncherAppsCallbackRegistered = false;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/TimeToLiveHelper.java b/services/core/java/com/android/server/notification/TimeToLiveHelper.java
new file mode 100644
index 0000000..2facab7
--- /dev/null
+++ b/services/core/java/com/android/server/notification/TimeToLiveHelper.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
+import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.util.Pair;
+import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.PackageManagerService;
+
+import java.io.PrintWriter;
+import java.util.TreeSet;
+
+/**
+ * Handles canceling notifications when their time to live expires
+ */
+@FlaggedApi(Flags.FLAG_ALL_NOTIFS_NEED_TTL)
+public class TimeToLiveHelper {
+ private static final String TAG = TimeToLiveHelper.class.getSimpleName();
+ private static final String ACTION = "com.android.server.notification.TimeToLiveHelper";
+
+ private static final int REQUEST_CODE_TIMEOUT = 1;
+ private static final String SCHEME_TIMEOUT = "timeout";
+ static final String EXTRA_KEY = "key";
+ private final Context mContext;
+ private final NotificationManagerPrivate mNm;
+ private final AlarmManager mAm;
+
+ @VisibleForTesting
+ final TreeSet<Pair<Long, String>> mKeys;
+
+ public TimeToLiveHelper(NotificationManagerPrivate nm, Context context) {
+ mContext = context;
+ mNm = nm;
+ mAm = context.getSystemService(AlarmManager.class);
+ mKeys = new TreeSet<>((left, right) -> Long.compare(left.first, right.first));
+
+ IntentFilter timeoutFilter = new IntentFilter(ACTION);
+ timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
+ mContext.registerReceiver(mNotificationTimeoutReceiver, timeoutFilter,
+ Context.RECEIVER_NOT_EXPORTED);
+ }
+
+ void destroy() {
+ mContext.unregisterReceiver(mNotificationTimeoutReceiver);
+ }
+
+ void dump(PrintWriter pw, String indent) {
+ pw.println(indent + "mKeys " + mKeys);
+ }
+
+ private @NonNull PendingIntent getAlarmPendingIntent(String nextKey, int flags) {
+ flags |= PendingIntent.FLAG_IMMUTABLE;
+ return PendingIntent.getBroadcast(mContext,
+ REQUEST_CODE_TIMEOUT,
+ new Intent(ACTION)
+ .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
+ .setData(new Uri.Builder()
+ .scheme(SCHEME_TIMEOUT)
+ .appendPath(nextKey)
+ .build())
+ .putExtra(EXTRA_KEY, nextKey)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
+ flags);
+ }
+
+ @VisibleForTesting
+ void scheduleTimeoutLocked(NotificationRecord record, long currentTime) {
+ removeMatchingEntry(record.getKey());
+
+ final long timeoutAfter = currentTime + record.getNotification().getTimeoutAfter();
+ if (record.getNotification().getTimeoutAfter() > 0) {
+ final Long currentEarliestTime = mKeys.isEmpty() ? null : mKeys.first().first;
+
+ // Maybe replace alarm with an earlier one
+ if (currentEarliestTime == null || timeoutAfter < currentEarliestTime) {
+ if (currentEarliestTime != null) {
+ cancelFirstAlarm();
+ }
+ mKeys.add(Pair.create(timeoutAfter, record.getKey()));
+ maybeScheduleFirstAlarm();
+ } else {
+ mKeys.add(Pair.create(timeoutAfter, record.getKey()));
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void cancelScheduledTimeoutLocked(NotificationRecord record) {
+ removeMatchingEntry(record.getKey());
+ }
+
+ private void removeMatchingEntry(String key) {
+ if (!mKeys.isEmpty() && key.equals(mKeys.first().second)) {
+ // cancel the first alarm, remove the first entry, maybe schedule the alarm for the new
+ // first entry
+ cancelFirstAlarm();
+ mKeys.remove(mKeys.first());
+ maybeScheduleFirstAlarm();
+ } else {
+ // just remove the entry
+ Pair<Long, String> trackedPair = null;
+ for (Pair<Long, String> entry : mKeys) {
+ if (key.equals(entry.second)) {
+ trackedPair = entry;
+ break;
+ }
+ }
+ if (trackedPair != null) {
+ mKeys.remove(trackedPair);
+ }
+ }
+ }
+
+ private void cancelFirstAlarm() {
+ final PendingIntent pi = getAlarmPendingIntent(mKeys.first().second, FLAG_CANCEL_CURRENT);
+ mAm.cancel(pi);
+ }
+
+ private void maybeScheduleFirstAlarm() {
+ if (!mKeys.isEmpty()) {
+ final PendingIntent piNewFirst = getAlarmPendingIntent(mKeys.first().second,
+ FLAG_UPDATE_CURRENT);
+ mAm.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mKeys.first().first, piNewFirst);
+ }
+ }
+
+ @VisibleForTesting
+ final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+ if (ACTION.equals(action)) {
+ Pair<Long, String> earliest = mKeys.first();
+ String key = intent.getStringExtra(EXTRA_KEY);
+ if (!earliest.second.equals(key)) {
+ Slog.wtf(TAG, "Alarm triggered but wasn't the earliest we were tracking");
+ }
+ removeMatchingEntry(key);
+ mNm.timeoutNotification(earliest.second);
+ }
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 4b8e485..6c93fe7 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -32,6 +32,7 @@
import static android.os.Trace.TRACE_TAG_RRO;
import static android.os.Trace.traceBegin;
import static android.os.Trace.traceEnd;
+
import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
import android.annotation.NonNull;
@@ -279,7 +280,8 @@
HandlerThread packageMonitorThread = new HandlerThread(TAG);
packageMonitorThread.start();
- mPackageMonitor.register(context, packageMonitorThread.getLooper(), true);
+ mPackageMonitor.register(
+ context, packageMonitorThread.getLooper(), UserHandle.ALL, true);
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(ACTION_USER_ADDED);
@@ -369,17 +371,17 @@
@Override
public void onPackageAppearedWithExtras(String packageName, Bundle extras) {
- handlePackageAdd(packageName, extras);
+ handlePackageAdd(packageName, extras, getChangingUserId());
}
@Override
public void onPackageChangedWithExtras(String packageName, Bundle extras) {
- handlePackageChange(packageName, extras);
+ handlePackageChange(packageName, extras, getChangingUserId());
}
@Override
public void onPackageDisappearedWithExtras(String packageName, Bundle extras) {
- handlePackageRemove(packageName, extras);
+ handlePackageRemove(packageName, extras, getChangingUserId());
}
}
@@ -393,54 +395,45 @@
return userIds;
}
- private void handlePackageAdd(String packageName, Bundle extras) {
+ private void handlePackageAdd(String packageName, Bundle extras, int userId) {
final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
- final int uid = extras.getInt(Intent.EXTRA_UID, 0);
- final int[] userIds = getUserIds(uid);
if (replacing) {
- onPackageReplaced(packageName, userIds);
+ onPackageReplaced(packageName, userId);
} else {
- onPackageAdded(packageName, userIds);
+ onPackageAdded(packageName, userId);
}
}
- private void handlePackageChange(String packageName, Bundle extras) {
- final int uid = extras.getInt(Intent.EXTRA_UID, 0);
- final int[] userIds = getUserIds(uid);
+ private void handlePackageChange(String packageName, Bundle extras, int userId) {
if (!ACTION_OVERLAY_CHANGED.equals(extras.getString(EXTRA_REASON))) {
- onPackageChanged(packageName, userIds);
+ onPackageChanged(packageName, userId);
}
}
- private void handlePackageRemove(String packageName, Bundle extras) {
+ private void handlePackageRemove(String packageName, Bundle extras, int userId) {
final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
final boolean systemUpdateUninstall =
extras.getBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, false);
- final int uid = extras.getInt(Intent.EXTRA_UID, 0);
- final int[] userIds = getUserIds(uid);
if (replacing) {
- onPackageReplacing(packageName, systemUpdateUninstall, userIds);
+ onPackageReplacing(packageName, systemUpdateUninstall, userId);
} else {
- onPackageRemoved(packageName, userIds);
+ onPackageRemoved(packageName, userId);
}
}
- private void onPackageAdded(@NonNull final String packageName,
- @NonNull final int[] userIds) {
+ private void onPackageAdded(@NonNull final String packageName, final int userId) {
try {
traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
- for (final int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageAdded(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(
- mImpl.onPackageAdded(packageName, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageAdded internal error", e);
- }
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageAdded(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(
+ mImpl.onPackageAdded(packageName, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageAdded internal error", e);
}
}
}
@@ -449,21 +442,18 @@
}
}
- private void onPackageChanged(@NonNull final String packageName,
- @NonNull final int[] userIds) {
+ private void onPackageChanged(@NonNull final String packageName, final int userId) {
try {
traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageUpdated(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(
- mImpl.onPackageChanged(packageName, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageChanged internal error", e);
- }
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(
+ mImpl.onPackageChanged(packageName, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageChanged internal error", e);
}
}
}
@@ -473,20 +463,18 @@
}
private void onPackageReplacing(@NonNull final String packageName,
- boolean systemUpdateUninstall, @NonNull final int[] userIds) {
+ boolean systemUpdateUninstall, final int userId) {
try {
traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageUpdated(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName,
- systemUpdateUninstall, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageReplacing internal error", e);
- }
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName,
+ systemUpdateUninstall, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageReplacing internal error", e);
}
}
}
@@ -495,21 +483,18 @@
}
}
- private void onPackageReplaced(@NonNull final String packageName,
- @NonNull final int[] userIds) {
+ private void onPackageReplaced(@NonNull final String packageName, final int userId) {
try {
traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageUpdated(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(
- mImpl.onPackageReplaced(packageName, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageReplaced internal error", e);
- }
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(
+ mImpl.onPackageReplaced(packageName, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageReplaced internal error", e);
}
}
}
@@ -518,15 +503,12 @@
}
}
- private void onPackageRemoved(@NonNull final String packageName,
- @NonNull final int[] userIds) {
+ private void onPackageRemoved(@NonNull final String packageName, final int userId) {
try {
traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- mPackageManager.onPackageRemoved(packageName, userId);
- updateTargetPackagesLocked(mImpl.onPackageRemoved(packageName, userId));
- }
+ synchronized (mLock) {
+ mPackageManager.onPackageRemoved(packageName, userId);
+ updateTargetPackagesLocked(mImpl.onPackageRemoved(packageName, userId));
}
} finally {
traceEnd(TRACE_TAG_RRO);
diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
index 5ebcca8..2c14532 100644
--- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
@@ -275,10 +275,7 @@
if (mFrpEnforced) {
automaticallyDeactivateFrpIfPossible();
setOemUnlockEnabledProperty(doGetOemUnlockEnabled());
- // Set the SECURE_FRP_MODE flag, for backward compatibility with clients who use it.
- // They should switch to calling #isFrpActive().
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.SECURE_FRP_MODE, mFrpActive ? 1 : 0);
+ setOldSettingForBackworkCompatibility(mFrpActive);
} else {
formatIfOemUnlockEnabled();
}
@@ -292,6 +289,13 @@
mInitDoneSignal.countDown();
}
+ private void setOldSettingForBackworkCompatibility(boolean isActive) {
+ // Set the SECURE_FRP_MODE flag, for backward compatibility with clients who use it.
+ // They should switch to calling #isFrpActive().
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.SECURE_FRP_MODE, isActive ? 1 : 0);
+ }
+
private void setOemUnlockEnabledProperty(boolean oemUnlockEnabled) {
setProperty(OEM_UNLOCK_PROP, oemUnlockEnabled ? "1" : "0");
}
@@ -628,6 +632,7 @@
Slog.w(TAG, "Upgrading from Android 14 or lower, defaulting FRP secret");
writeFrpMagicAndDefaultSecret();
mFrpActive = false;
+ setOldSettingForBackworkCompatibility(mFrpActive);
return true;
}
@@ -699,6 +704,7 @@
void activateFrp() {
synchronized (mLock) {
mFrpActive = true;
+ setOldSettingForBackworkCompatibility(mFrpActive);
}
}
@@ -740,6 +746,7 @@
if (MessageDigest.isEqual(secret, partitionSecret)) {
mFrpActive = false;
Slog.i(TAG, "FRP secret matched, FRP deactivated.");
+ setOldSettingForBackworkCompatibility(mFrpActive);
return true;
} else {
Slog.e(TAG,
@@ -1315,6 +1322,7 @@
public boolean deactivateFactoryResetProtectionWithoutSecret() {
synchronized (mLock) {
mFrpActive = false;
+ setOldSettingForBackworkCompatibility(/* isActive */ mFrpActive);
}
return true;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 68cd3e4..614828a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5753,17 +5753,22 @@
@Override
public void setApplicationCategoryHint(String packageName, int categoryHint,
String callerPackageName) {
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getCallingUserId();
final FunctionalUtils.ThrowingBiFunction<PackageStateMutator.InitialState, Computer,
PackageStateMutator.Result> implementation = (initialState, computer) -> {
- if (computer.getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ if (computer.getInstantAppPackageName(callingUid) != null) {
throw new SecurityException(
"Instant applications don't have access to this method");
}
- mInjector.getSystemService(AppOpsManager.class)
- .checkPackage(Binder.getCallingUid(), callerPackageName);
+ final int callerPackageUid = computer.getPackageUid(callerPackageName, 0, userId);
+ if (callerPackageUid != callingUid) {
+ throw new SecurityException(
+ "Package " + callerPackageName + " does not belong to " + callingUid);
+ }
PackageStateInternal packageState = computer.getPackageStateForInstalledAndFiltered(
- packageName, Binder.getCallingUid(), UserHandle.getCallingUserId());
+ packageName, callingUid, userId);
if (packageState == null) {
throw new IllegalArgumentException("Unknown target package " + packageName);
}
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 1fe49c7..7ef7ce7 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.PARSE_APEX;
+
import android.apex.ApexInfo;
import android.apex.ApexInfoList;
import android.apex.ApexSessionInfo;
@@ -399,7 +401,7 @@
final ParsedPackage parsedPackage;
try (PackageParser2 packageParser = mPackageParserSupplier.get()) {
File apexFile = new File(apexInfo.modulePath);
- parsedPackage = packageParser.parsePackage(apexFile, 0, false);
+ parsedPackage = packageParser.parsePackage(apexFile, PARSE_APEX, false);
} catch (PackageParserException e) {
throw new PackageManagerException(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 4c653f6..fe9c3f2 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -50,6 +50,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.IShortcutService;
import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ShortcutChangeCallback;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -151,6 +152,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
@@ -320,12 +322,11 @@
private final Handler mHandler;
- @GuardedBy("mLock")
- private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
+ private final CopyOnWriteArrayList<ShortcutChangeListener> mListeners =
+ new CopyOnWriteArrayList<>();
- @GuardedBy("mLock")
- private final ArrayList<LauncherApps.ShortcutChangeCallback> mShortcutChangeCallbacks =
- new ArrayList<>(1);
+ private final CopyOnWriteArrayList<ShortcutChangeCallback> mShortcutChangeCallbacks =
+ new CopyOnWriteArrayList<>();
private final AtomicLong mRawLastResetTime = new AtomicLong(0);
@@ -1841,18 +1842,11 @@
@UserIdInt final int userId) {
return () -> {
try {
- final ArrayList<ShortcutChangeListener> copy;
- synchronized (mLock) {
- if (!isUserUnlockedL(userId)) {
- return;
- }
-
- copy = new ArrayList<>(mListeners);
+ if (!isUserUnlockedL(userId)) {
+ return;
}
// Note onShortcutChanged() needs to be called with the system service permissions.
- for (int i = copy.size() - 1; i >= 0; i--) {
- copy.get(i).onShortcutChanged(packageName, userId);
- }
+ mListeners.forEach(listener -> listener.onShortcutChanged(packageName, userId));
} catch (Exception ignore) {
}
};
@@ -1867,22 +1861,17 @@
final UserHandle user = UserHandle.of(userId);
injectPostToHandler(() -> {
try {
- final ArrayList<LauncherApps.ShortcutChangeCallback> copy;
- synchronized (mLock) {
- if (!isUserUnlockedL(userId)) {
- return;
- }
-
- copy = new ArrayList<>(mShortcutChangeCallbacks);
+ if (!isUserUnlockedL(userId)) {
+ return;
}
- for (int i = copy.size() - 1; i >= 0; i--) {
+ mShortcutChangeCallbacks.forEach(callback -> {
if (!CollectionUtils.isEmpty(changedList)) {
- copy.get(i).onShortcutsAddedOrUpdated(packageName, changedList, user);
+ callback.onShortcutsAddedOrUpdated(packageName, changedList, user);
}
if (!CollectionUtils.isEmpty(removedList)) {
- copy.get(i).onShortcutsRemoved(packageName, removedList, user);
+ callback.onShortcutsRemoved(packageName, removedList, user);
}
- }
+ });
} catch (Exception ignore) {
}
});
@@ -3425,17 +3414,13 @@
@Override
public void addListener(@NonNull ShortcutChangeListener listener) {
- synchronized (mLock) {
- mListeners.add(Objects.requireNonNull(listener));
- }
+ mListeners.add(Objects.requireNonNull(listener));
}
@Override
public void addShortcutChangeCallback(
@NonNull LauncherApps.ShortcutChangeCallback callback) {
- synchronized (mLock) {
- mShortcutChangeCallbacks.add(Objects.requireNonNull(callback));
- }
+ mShortcutChangeCallbacks.add(Objects.requireNonNull(callback));
}
@Override
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 33b5a70..1e7fdfe 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -306,7 +306,7 @@
R.color.black)
.setDarkThemeBadgeColors(
R.color.white)
- .setDefaultRestrictions(getDefaultProfileRestrictions())
+ .setDefaultRestrictions(getDefaultPrivateProfileRestrictions())
.setDefaultCrossProfileIntentFilters(getDefaultPrivateCrossProfileIntentFilter())
.setDefaultUserProperties(new UserProperties.Builder()
.setStartWithParent(true)
@@ -430,6 +430,13 @@
return restrictions;
}
+ @VisibleForTesting
+ static Bundle getDefaultPrivateProfileRestrictions() {
+ final Bundle restrictions = getDefaultProfileRestrictions();
+ restrictions.putBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING, true);
+ return restrictions;
+ }
+
private static Bundle getDefaultManagedProfileSecureSettings() {
// Only add String values to the bundle, settings are written as Strings eventually
final Bundle settings = new Bundle();
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index f7f76aa..57ea233 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -807,7 +807,7 @@
getDefaultSystemHandlerActivityPackage(pm,
SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId),
userId, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
- NOTIFICATION_PERMISSIONS, PHONE_PERMISSIONS);
+ NOTIFICATION_PERMISSIONS, PHONE_PERMISSIONS, CALENDAR_PERMISSIONS);
}
// Voice recognition
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 bd0501d..20c5b5f 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -462,6 +462,11 @@
}
@Override
+ public int getNumRegisteredAttributionSources(int uid) {
+ return mAttributionSourceRegistry.getNumRegisteredAttributionSources(uid);
+ }
+
+ @Override
public List<String> getAutoRevokeExemptionRequestedPackages(int userId) {
return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISCOURAGED, userId);
}
@@ -938,6 +943,26 @@
}
}
+ public int getNumRegisteredAttributionSources(int uid) {
+ mContext.enforceCallingOrSelfPermission(UPDATE_APP_OPS_STATS,
+ "getting the number of registered AttributionSources requires "
+ + "UPDATE_APP_OPS_STATS");
+ // Influence the system to perform a garbage collection, so the provided number is as
+ // accurate as possible
+ System.gc();
+ System.gc();
+ synchronized (mLock) {
+ int[] numForUid = { 0 };
+ mAttributions.forEach((key, value) -> {
+ if (value.getUid() == uid) {
+ numForUid[0]++;
+ }
+
+ });
+ return numForUid[0];
+ }
+ }
+
private int resolveUid(int uid) {
final VoiceInteractionManagerInternal vimi = LocalServices
.getService(VoiceInteractionManagerInternal.class);
diff --git a/services/core/java/com/android/server/power/FaceDownDetector.java b/services/core/java/com/android/server/power/FaceDownDetector.java
index b237ca2..84ed87a 100644
--- a/services/core/java/com/android/server/power/FaceDownDetector.java
+++ b/services/core/java/com/android/server/power/FaceDownDetector.java
@@ -76,6 +76,8 @@
private static final boolean DEFAULT_FEATURE_ENABLED = true;
private boolean mIsEnabled;
+ // Defaults to true, we only want to disable if this is specifically requested.
+ private boolean mEnabledOverride = true;
private int mSensorMaxLatencyMicros;
@@ -240,6 +242,7 @@
pw.println(" mZAccelerationThreshold=" + mZAccelerationThreshold);
pw.println(" mAccelerationThreshold=" + mAccelerationThreshold);
pw.println(" mTimeThreshold=" + mTimeThreshold);
+ pw.println(" mEnabledOverride=" + mEnabledOverride);
}
@Override
@@ -336,10 +339,9 @@
}
private boolean isEnabled() {
- return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_FEATURE_ENABLED,
- DEFAULT_FEATURE_ENABLED)
- && mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_flipToScreenOffEnabled);
+ return mEnabledOverride && DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_FEATURE_ENABLED, DEFAULT_FEATURE_ENABLED) && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_flipToScreenOffEnabled);
}
private float getAccelerationThreshold() {
@@ -450,6 +452,15 @@
}
/**
+ * Allows detector to be enabled & disabled.
+ * @param enabled whether to enable detector.
+ */
+ public void setEnabledOverride(boolean enabled) {
+ mEnabledOverride = enabled;
+ mIsEnabled = isEnabled();
+ }
+
+ /**
* Sets how much screen on time might be saved as a result of this detector. Currently used for
* logging purposes.
*/
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b5d49b3..eb1f720 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -6921,6 +6921,16 @@
Binder.restoreCallingIdentity(ident);
}
}
+
+ public void setUseFaceDownDetector(boolean enable) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mFaceDownDetector.setEnabledOverride(enable);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/power/PowerManagerShellCommand.java b/services/core/java/com/android/server/power/PowerManagerShellCommand.java
index 9439b76..20184e9f 100644
--- a/services/core/java/com/android/server/power/PowerManagerShellCommand.java
+++ b/services/core/java/com/android/server/power/PowerManagerShellCommand.java
@@ -63,6 +63,8 @@
return runListAmbientDisplaySuppressionTokens();
case "set-prox":
return runSetProx();
+ case "set-face-down-detector":
+ return runSetFaceDownDetector();
default:
return handleDefaultCommands(cmd);
}
@@ -178,6 +180,20 @@
return 0;
}
+ /**
+ * To be used for testing - allowing us to disable the usage of face down detector.
+ */
+ private int runSetFaceDownDetector() {
+ try {
+ mService.setUseFaceDownDetector(Boolean.parseBoolean(getNextArgRequired()));
+ } catch (Exception e) {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Error: " + e);
+ return -1;
+ }
+ return 0;
+ }
+
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -203,6 +219,8 @@
pw.println(" Acquires the proximity sensor wakelock. Wakelock is associated with");
pw.println(" a specific display if specified. 'list' lists wakelocks previously");
pw.println(" created by set-prox including their held status.");
+ pw.println(" set-face-down-detector [true|false]");
+ pw.println(" sets whether we use face down detector timeouts or not");
pw.println();
Intent.printIntentArgsHelp(pw , "");
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
index 894226c..e1b4b88 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
@@ -34,11 +34,14 @@
import java.io.IOException;
import java.io.StringWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.TimeZone;
/**
* This class represents aggregated power stats for a variety of power components (CPU, WiFi,
@@ -66,7 +69,7 @@
aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs();
mPowerComponentStats = new PowerComponentAggregatedPowerStats[configs.size()];
for (int i = 0; i < configs.size(); i++) {
- mPowerComponentStats[i] = new PowerComponentAggregatedPowerStats(configs.get(i));
+ mPowerComponentStats[i] = new PowerComponentAggregatedPowerStats(this, configs.get(i));
}
}
@@ -223,7 +226,7 @@
if (i == 0) {
baseTime = clockUpdate.monotonicTime;
sb.append("Start time: ")
- .append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", clockUpdate.currentTime))
+ .append(formatDateTime(clockUpdate.currentTime))
.append(" (")
.append(baseTime)
.append(") duration: ")
@@ -235,8 +238,7 @@
TimeUtils.formatDuration(
clockUpdate.monotonicTime - baseTime, sb,
TimeUtils.HUNDRED_DAY_FIELD_LEN + 3);
- sb.append(" ").append(
- DateFormat.format("yyyy-MM-dd-HH-mm-ss", clockUpdate.currentTime));
+ sb.append(" ").append(formatDateTime(clockUpdate.currentTime));
ipw.increaseIndent();
ipw.println(sb);
ipw.decreaseIndent();
@@ -267,6 +269,12 @@
}
}
+ private static String formatDateTime(long timeInMillis) {
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
+ format.getCalendar().setTimeZone(TimeZone.getTimeZone("GMT"));
+ return format.format(new Date(timeInMillis));
+ }
+
@Override
public String toString() {
StringWriter sw = new StringWriter();
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
index 6fbbc0f..5aad570 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
@@ -75,7 +75,7 @@
private final int mPowerComponentId;
private @TrackedState int[] mTrackedDeviceStates;
private @TrackedState int[] mTrackedUidStates;
- private AggregatedPowerStatsProcessor mProcessor = NO_OP_PROCESSOR;
+ private PowerStatsProcessor mProcessor = NO_OP_PROCESSOR;
PowerComponent(int powerComponentId) {
this.mPowerComponentId = powerComponentId;
@@ -85,6 +85,9 @@
* Configures which states should be tracked as separate dimensions for the entire device.
*/
public PowerComponent trackDeviceStates(@TrackedState int... states) {
+ if (mTrackedDeviceStates != null) {
+ throw new IllegalStateException("Component is already configured");
+ }
mTrackedDeviceStates = states;
return this;
}
@@ -93,6 +96,9 @@
* Configures which states should be tracked as separate dimensions on a per-UID basis.
*/
public PowerComponent trackUidStates(@TrackedState int... states) {
+ if (mTrackedUidStates != null) {
+ throw new IllegalStateException("Component is already configured");
+ }
mTrackedUidStates = states;
return this;
}
@@ -102,7 +108,7 @@
* before giving the aggregates stats to consumers. The processor can complete the
* aggregation process, for example by computing estimated power usage.
*/
- public PowerComponent setProcessor(@NonNull AggregatedPowerStatsProcessor processor) {
+ public PowerComponent setProcessor(@NonNull PowerStatsProcessor processor) {
mProcessor = processor;
return this;
}
@@ -137,7 +143,7 @@
}
@NonNull
- public AggregatedPowerStatsProcessor getProcessor() {
+ public PowerStatsProcessor getProcessor() {
return mProcessor;
}
@@ -153,6 +159,7 @@
}
return false;
}
+
}
private final List<PowerComponent> mPowerComponents = new ArrayList<>();
@@ -168,23 +175,55 @@
return builder;
}
+ /**
+ * Creates a configuration for the specified power component, which is a subcomponent
+ * of a different power component. The tracked states will be the same as the parent
+ * component's.
+ */
+ public PowerComponent trackPowerComponent(int powerComponentId,
+ int parentPowerComponentId) {
+ PowerComponent parent = null;
+ for (int i = 0; i < mPowerComponents.size(); i++) {
+ PowerComponent powerComponent = mPowerComponents.get(i);
+ if (powerComponent.getPowerComponentId() == parentPowerComponentId) {
+ parent = powerComponent;
+ break;
+ }
+ }
+
+ if (parent == null) {
+ throw new IllegalArgumentException(
+ "Parent component " + parentPowerComponentId + " is not configured");
+ }
+
+ PowerComponent powerComponent = trackPowerComponent(powerComponentId);
+ powerComponent.mTrackedDeviceStates = parent.mTrackedDeviceStates;
+ powerComponent.mTrackedUidStates = parent.mTrackedUidStates;
+ return powerComponent;
+ }
+
public List<PowerComponent> getPowerComponentsAggregatedStatsConfigs() {
return mPowerComponents;
}
- private static final AggregatedPowerStatsProcessor NO_OP_PROCESSOR =
- new AggregatedPowerStatsProcessor() {
+ private static final PowerStatsProcessor NO_OP_PROCESSOR =
+ new PowerStatsProcessor() {
@Override
- public void finish(PowerComponentAggregatedPowerStats stats) {
+ void finish(PowerComponentAggregatedPowerStats stats) {
}
@Override
- public String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
return Arrays.toString(stats);
}
@Override
- public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
+ return descriptor.getStateLabel(key) + " " + Arrays.toString(stats);
+ }
+
+ @Override
+ String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
return Arrays.toString(stats);
}
};
diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
index a8eda3c..cb10da9 100644
--- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
@@ -24,6 +24,7 @@
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.net.wifi.WifiManager;
+import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.OutcomeReceiver;
@@ -603,24 +604,31 @@
}
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
- // We were asked to fetch Telephony data.
- if (mTelephony != null) {
- CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>();
- mTelephony.requestModemActivityInfo(Runnable::run,
- new OutcomeReceiver<ModemActivityInfo,
- TelephonyManager.ModemActivityInfoException>() {
- @Override
- public void onResult(ModemActivityInfo result) {
- temp.complete(result);
- }
+ @SuppressWarnings("GuardedBy")
+ PowerStatsCollector collector = mStats.getPowerStatsCollector(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+ if (collector.isEnabled()) {
+ collector.schedule();
+ } else {
+ // We were asked to fetch Telephony data.
+ if (mTelephony != null) {
+ CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>();
+ mTelephony.requestModemActivityInfo(Runnable::run,
+ new OutcomeReceiver<ModemActivityInfo,
+ TelephonyManager.ModemActivityInfoException>() {
+ @Override
+ public void onResult(ModemActivityInfo result) {
+ temp.complete(result);
+ }
- @Override
- public void onError(TelephonyManager.ModemActivityInfoException e) {
- Slog.w(TAG, "error reading modem stats:" + e);
- temp.complete(null);
- }
- });
- modemFuture = temp;
+ @Override
+ public void onError(TelephonyManager.ModemActivityInfoException e) {
+ Slog.w(TAG, "error reading modem stats:" + e);
+ temp.complete(null);
+ }
+ });
+ modemFuture = temp;
+ }
}
if (!railUpdated) {
synchronized (mStats) {
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 3a84897..54cb9c9 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -22,6 +22,8 @@
import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
+import static com.android.server.power.stats.MobileRadioPowerStatsCollector.mapRadioAccessNetworkTypeToRadioAccessTechnology;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,6 +37,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.hardware.usb.UsbManager;
import android.location.GnssSignalQuality;
@@ -71,6 +74,7 @@
import android.os.connectivity.GpsBatteryStats;
import android.os.connectivity.WifiActivityEnergyInfo;
import android.os.connectivity.WifiBatteryStats;
+import android.power.PowerStatsInternal;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.NetworkType;
@@ -99,6 +103,7 @@
import android.util.Printer;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseDoubleArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
@@ -137,6 +142,7 @@
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.LocalServices;
import com.android.server.power.optimization.Flags;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
@@ -166,8 +172,12 @@
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
/**
* All information we are collecting about things that can happen that impact
@@ -281,7 +291,8 @@
private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
private int[] mCpuPowerBracketMap;
private final CpuPowerStatsCollector mCpuPowerStatsCollector;
- private boolean mPowerStatsCollectorEnabled;
+ private final MobileRadioPowerStatsCollector mMobileRadioPowerStatsCollector;
+ private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray();
public LongSparseArray<SamplingTimer> getKernelMemoryStats() {
return mKernelMemoryStats;
@@ -433,9 +444,11 @@
public static class BatteryStatsConfig {
static final int RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG = 1 << 0;
static final int RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG = 1 << 1;
+ static final long DEFAULT_POWER_STATS_COLLECTION_THROTTLE_PERIOD =
+ TimeUnit.HOURS.toMillis(1);
private final int mFlags;
- private final long mPowerStatsThrottlePeriodCpu;
+ private SparseLongArray mPowerStatsThrottlePeriods;
private BatteryStatsConfig(Builder builder) {
int flags = 0;
@@ -446,7 +459,7 @@
flags |= RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
}
mFlags = flags;
- mPowerStatsThrottlePeriodCpu = builder.mPowerStatsThrottlePeriodCpu;
+ mPowerStatsThrottlePeriods = builder.mPowerStatsThrottlePeriods;
}
/**
@@ -467,8 +480,9 @@
== RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
}
- long getPowerStatsThrottlePeriodCpu() {
- return mPowerStatsThrottlePeriodCpu;
+ long getPowerStatsThrottlePeriod(@BatteryConsumer.PowerComponent int powerComponent) {
+ return mPowerStatsThrottlePeriods.get(powerComponent,
+ DEFAULT_POWER_STATS_COLLECTION_THROTTLE_PERIOD);
}
/**
@@ -477,12 +491,16 @@
public static class Builder {
private boolean mResetOnUnplugHighBatteryLevel;
private boolean mResetOnUnplugAfterSignificantCharge;
- private long mPowerStatsThrottlePeriodCpu;
+ private SparseLongArray mPowerStatsThrottlePeriods;
public Builder() {
mResetOnUnplugHighBatteryLevel = true;
mResetOnUnplugAfterSignificantCharge = true;
- mPowerStatsThrottlePeriodCpu = 60000;
+ mPowerStatsThrottlePeriods = new SparseLongArray();
+ setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_CPU,
+ TimeUnit.MINUTES.toMillis(1));
+ setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ TimeUnit.HOURS.toMillis(1));
}
/**
@@ -512,10 +530,11 @@
/**
* Sets the minimum amount of time (in millis) to wait between passes
- * of CPU power stats collection.
+ * of power stats collection for the specified power component.
*/
- public Builder setPowerStatsThrottlePeriodCpu(long periodMs) {
- mPowerStatsThrottlePeriodCpu = periodMs;
+ public Builder setPowerStatsThrottlePeriodMillis(
+ @BatteryConsumer.PowerComponent int powerComponent, long periodMs) {
+ mPowerStatsThrottlePeriods.put(powerComponent, periodMs);
return this;
}
}
@@ -597,7 +616,7 @@
@SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter
@VisibleForTesting
public void updateProcStateCpuTimesLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
- if (mPowerStatsCollectorEnabled) {
+ if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
return;
}
@@ -653,7 +672,7 @@
*/
@SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter
public void updateCpuTimesForAllUids() {
- if (mPowerStatsCollectorEnabled && mCpuPowerStatsCollector != null) {
+ if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
mCpuPowerStatsCollector.schedule();
return;
}
@@ -713,7 +732,8 @@
@GuardedBy("this")
private void ensureKernelSingleUidTimeReaderLocked() {
- if (mPowerStatsCollectorEnabled || mKernelSingleUidTimeReader != null) {
+ if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)
+ || mKernelSingleUidTimeReader != null) {
return;
}
@@ -830,8 +850,6 @@
private final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
private final HistoryStepDetailsCalculatorImpl mStepDetailsCalculator =
new HistoryStepDetailsCalculatorImpl();
- private final PowerStats.DescriptorRegistry mPowerStatsDescriptorRegistry =
- new PowerStats.DescriptorRegistry();
private boolean mHaveBatteryLevel = false;
private boolean mBatteryPluggedIn;
@@ -1759,7 +1777,7 @@
return mMaxLearnedBatteryCapacityUah;
}
- public class FrameworkStatsLogger {
+ public static class FrameworkStatsLogger {
public void uidProcessStateChanged(int uid, int state) {
// TODO(b/155216561): It is possible for isolated uids to be in a higher
// state than its parent uid. We should track the highest state within the union of host
@@ -1768,25 +1786,24 @@
ActivityManager.processStateAmToProto(state));
}
- public void wakelockStateChanged(int uid, WorkChain wc, String name, int type,
- int procState, boolean acquired) {
+ public void wakelockStateChanged(int uid, WorkChain wc, String name,
+ int procState, boolean acquired, int powerManagerWakeLockLevel) {
int event = acquired
? FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE
: FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE;
if (wc != null) {
FrameworkStatsLog.write(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(),
- wc.getTags(), getPowerManagerWakeLockLevel(type), name,
- event, procState);
+ wc.getTags(), powerManagerWakeLockLevel, name, event, procState);
} else {
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED,
- mapIsolatedUid(uid), null, getPowerManagerWakeLockLevel(type), name,
- event, procState);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, uid,
+ null, powerManagerWakeLockLevel, name, event, procState);
}
}
- public void kernelWakeupReported(long deltaUptimeUs) {
- FrameworkStatsLog.write(FrameworkStatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason,
- /* duration_usec */ deltaUptimeUs, mLastWakeupElapsedTimeMs);
+ public void kernelWakeupReported(long deltaUptimeUs, String lastWakeupReason,
+ long lastWakeupElapsedTimeMs) {
+ FrameworkStatsLog.write(FrameworkStatsLog.KERNEL_WAKEUP_REPORTED, lastWakeupReason,
+ /* duration_usec */ deltaUptimeUs, lastWakeupElapsedTimeMs);
}
public void gpsScanStateChanged(int uid, WorkChain workChain, boolean stateOn) {
@@ -1838,44 +1855,18 @@
FrameworkStatsLog.write(
FrameworkStatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, strengthBin);
}
+
+ /**
+ * Records a statsd event when the batterystats config file is written to disk.
+ */
+ public void writeCommitSysConfigFile(String fileName, long durationMs) {
+ com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(fileName,
+ durationMs);
+ }
}
private final FrameworkStatsLogger mFrameworkStatsLogger;
- @VisibleForTesting
- public BatteryStatsImpl(Clock clock, File historyDirectory, @NonNull Handler handler,
- @NonNull PowerStatsUidResolver powerStatsUidResolver,
- @NonNull FrameworkStatsLogger frameworkStatsLogger,
- @NonNull BatteryStatsHistory.TraceDelegate traceDelegate,
- @NonNull BatteryStatsHistory.EventLogger eventLogger) {
- mClock = clock;
- initKernelStatsReaders();
- mBatteryStatsConfig = new BatteryStatsConfig.Builder().build();
- mHandler = handler;
- mPowerStatsUidResolver = powerStatsUidResolver;
- mFrameworkStatsLogger = frameworkStatsLogger;
- mConstants = new Constants(mHandler);
- mStartClockTimeMs = clock.currentTimeMillis();
- mDailyFile = null;
- mMonotonicClock = new MonotonicClock(0, mClock);
- if (historyDirectory == null) {
- mCheckinFile = null;
- mStatsFile = null;
- mHistory = new BatteryStatsHistory(mConstants.MAX_HISTORY_BUFFER,
- mStepDetailsCalculator, mClock, mMonotonicClock, traceDelegate, eventLogger);
- } else {
- mCheckinFile = new AtomicFile(new File(historyDirectory, "batterystats-checkin.bin"));
- mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin"));
- mHistory = new BatteryStatsHistory(historyDirectory, mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock,
- traceDelegate, eventLogger);
- }
- mPlatformIdleStateCallback = null;
- mEnergyConsumerRetriever = null;
- mUserInfoProvider = null;
- mCpuPowerStatsCollector = null;
- }
-
private void initKernelStatsReaders() {
if (!isKernelStatsAvailable()) {
return;
@@ -1893,6 +1884,92 @@
mTmpRailStats = new RailStats();
}
+ private class PowerStatsCollectorInjector implements CpuPowerStatsCollector.Injector,
+ MobileRadioPowerStatsCollector.Injector {
+ private PackageManager mPackageManager;
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ private NetworkStatsManager mNetworkStatsManager;
+ private TelephonyManager mTelephonyManager;
+
+ void setContext(Context context) {
+ mPackageManager = context.getPackageManager();
+ mConsumedEnergyRetriever = new PowerStatsCollector.ConsumedEnergyRetrieverImpl(
+ LocalServices.getService(PowerStatsInternal.class));
+ mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class);
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ }
+
+ @Override
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ public Clock getClock() {
+ return mClock;
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mPowerStatsUidResolver;
+ }
+
+ @Override
+ public CpuScalingPolicies getCpuScalingPolicies() {
+ return mCpuScalingPolicies;
+ }
+
+ @Override
+ public PowerProfile getPowerProfile() {
+ return mPowerProfile;
+ }
+
+ @Override
+ public CpuPowerStatsCollector.KernelCpuStatsReader getKernelCpuStatsReader() {
+ return new CpuPowerStatsCollector.KernelCpuStatsReader();
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> mBatteryVoltageMv;
+ }
+
+ @Override
+ public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
+ return () -> readMobileNetworkStatsLocked(mNetworkStatsManager);
+ }
+
+ @Override
+ public TelephonyManager getTelephonyManager() {
+ return mTelephonyManager;
+ }
+
+ @Override
+ public LongSupplier getCallDurationSupplier() {
+ return () -> mPhoneOnTimer.getTotalTimeLocked(mClock.elapsedRealtime() * 1000,
+ STATS_SINCE_CHARGED);
+ }
+
+ @Override
+ public LongSupplier getPhoneSignalScanDurationSupplier() {
+ return () -> mPhoneSignalScanningTimer.getTotalTimeLocked(
+ mClock.elapsedRealtime() * 1000, STATS_SINCE_CHARGED);
+ }
+ }
+
+ private final PowerStatsCollectorInjector mPowerStatsCollectorInjector =
+ new PowerStatsCollectorInjector();
+
/**
* TimeBase observer.
*/
@@ -4808,7 +4885,6 @@
if (type == WAKE_TYPE_PARTIAL) {
// Only care about partial wake locks, since full wake locks
// will be canceled when the user puts the screen to sleep.
- aggregateLastWakeupUptimeLocked(elapsedRealtimeMs, uptimeMs);
if (historyName == null) {
historyName = name;
}
@@ -4849,8 +4925,9 @@
Uid uidStats = getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs);
uidStats.noteStartWakeLocked(pid, name, type, elapsedRealtimeMs);
- mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name, type,
- uidStats.mProcessState, true /* acquired */);
+ mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name,
+ uidStats.mProcessState, true /* acquired */,
+ getPowerManagerWakeLockLevel(type));
}
}
@@ -4893,8 +4970,9 @@
Uid uidStats = getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs);
uidStats.noteStopWakeLocked(pid, name, type, elapsedRealtimeMs);
- mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name, type,
- uidStats.mProcessState, false /* acquired */);
+ mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name,
+ uidStats.mProcessState, false/* acquired */,
+ getPowerManagerWakeLockLevel(type));
if (mappedUid != uid) {
// Decrement the ref count for the isolated uid and delete the mapping if uneeded.
@@ -4910,8 +4988,8 @@
* TODO: Delete this. Instead, FrameworkStatsLog.write should be called from
* PowerManager's Notifier.
*/
- private int getPowerManagerWakeLockLevel(int battertStatsWakelockType) {
- switch (battertStatsWakelockType) {
+ private int getPowerManagerWakeLockLevel(int batteryStatsWakelockType) {
+ switch (batteryStatsWakelockType) {
// PowerManager.PARTIAL_WAKE_LOCK or PROXIMITY_SCREEN_OFF_WAKE_LOCK
case BatteryStats.WAKE_TYPE_PARTIAL:
return PowerManager.PARTIAL_WAKE_LOCK;
@@ -4929,7 +5007,7 @@
return -1;
default:
- Slog.e(TAG, "Illegal wakelock type in batterystats: " + battertStatsWakelockType);
+ Slog.e(TAG, "Illegal wakelock type in batterystats: " + batteryStatsWakelockType);
return -1;
}
}
@@ -5126,19 +5204,14 @@
}
@GuardedBy("this")
- void aggregateLastWakeupUptimeLocked(long elapsedRealtimeMs, long uptimeMs) {
+ public void noteWakeupReasonLocked(String reason, long elapsedRealtimeMs, long uptimeMs) {
if (mLastWakeupReason != null) {
long deltaUptimeMs = uptimeMs - mLastWakeupUptimeMs;
SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
timer.add(deltaUptimeMs * 1000, 1, elapsedRealtimeMs); // time in in microseconds
- mFrameworkStatsLogger.kernelWakeupReported(deltaUptimeMs * 1000);
- mLastWakeupReason = null;
+ mFrameworkStatsLogger.kernelWakeupReported(deltaUptimeMs * 1000, mLastWakeupReason,
+ mLastWakeupElapsedTimeMs);
}
- }
-
- @GuardedBy("this")
- public void noteWakeupReasonLocked(String reason, long elapsedRealtimeMs, long uptimeMs) {
- aggregateLastWakeupUptimeLocked(elapsedRealtimeMs, uptimeMs);
mHistory.recordWakeupEvent(elapsedRealtimeMs, uptimeMs, reason);
mLastWakeupReason = reason;
mLastWakeupUptimeMs = uptimeMs;
@@ -5738,16 +5811,19 @@
mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
- if (mLastModemActivityInfo != null) {
- if (elapsedRealtimeMs < mLastModemActivityInfo.getTimestampMillis()
+ if (mMobileRadioPowerStatsCollector.isEnabled()) {
+ mMobileRadioPowerStatsCollector.schedule();
+ } else {
+ // Check if modem Activity info has been collected recently, don't bother
+ // triggering another update.
+ if (mLastModemActivityInfo == null
+ || elapsedRealtimeMs >= mLastModemActivityInfo.getTimestampMillis()
+ MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS) {
- // Modem Activity info has been collected recently, don't bother
- // triggering another update.
- return false;
+ mExternalSync.scheduleSync("modem-data",
+ BatteryExternalStatsWorker.UPDATE_RADIO);
+ return true;
}
}
- // Tell the caller to collect radio network/power stats.
- return true;
}
}
return false;
@@ -5915,6 +5991,7 @@
mPhoneOnTimer.startRunningLocked(elapsedRealtimeMs);
if (mConstants.PHONE_ON_EXTERNAL_STATS_COLLECTION) {
scheduleSyncExternalStatsLocked("phone-on", ExternalStatsSync.UPDATE_RADIO);
+ mMobileRadioPowerStatsCollector.schedule();
}
}
}
@@ -5927,6 +6004,7 @@
mPhoneOn = false;
mPhoneOnTimer.stopRunningLocked(elapsedRealtimeMs);
scheduleSyncExternalStatsLocked("phone-off", ExternalStatsSync.UPDATE_RADIO);
+ mMobileRadioPowerStatsCollector.schedule();
}
}
@@ -6269,27 +6347,6 @@
}
}
- @RadioAccessTechnology
- private static int mapRadioAccessNetworkTypeToRadioAccessTechnology(
- @AccessNetworkConstants.RadioAccessNetworkType int dataType) {
- switch (dataType) {
- case AccessNetworkConstants.AccessNetworkType.NGRAN:
- return RADIO_ACCESS_TECHNOLOGY_NR;
- case AccessNetworkConstants.AccessNetworkType.EUTRAN:
- return RADIO_ACCESS_TECHNOLOGY_LTE;
- case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough
- case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough
- case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough
- case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough
- case AccessNetworkConstants.AccessNetworkType.IWLAN:
- return RADIO_ACCESS_TECHNOLOGY_OTHER;
- default:
- Slog.w(TAG,
- "Unhandled RadioAccessNetworkType (" + dataType + "), mapping to OTHER");
- return RADIO_ACCESS_TECHNOLOGY_OTHER;
- }
- }
-
@GuardedBy("this")
public void noteWifiOnLocked(long elapsedRealtimeMs, long uptimeMs) {
if (!mWifiOn) {
@@ -8311,7 +8368,7 @@
@GuardedBy("mBsi")
private void ensureMultiStateCounters(long timestampMs) {
- if (mBsi.mPowerStatsCollectorEnabled) {
+ if (mBsi.mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
throw new IllegalStateException("Multi-state counters used in streamlined mode");
}
@@ -10612,7 +10669,8 @@
mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtimeMs);
}
- if (!mBsi.mPowerStatsCollectorEnabled && mBsi.trackPerProcStateCpuTimes()) {
+ if (!mBsi.mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)
+ && mBsi.trackPerProcStateCpuTimes()) {
mBsi.updateProcStateCpuTimesLocked(mUid, elapsedRealtimeMs, uptimeMs);
LongArrayMultiStateCounter onBatteryCounter =
@@ -10634,7 +10692,8 @@
final int batteryConsumerProcessState =
mapUidProcessStateToBatteryConsumerProcessState(uidRunningState);
- if (mBsi.mSystemReady && mBsi.mPowerStatsCollectorEnabled) {
+ if (mBsi.mSystemReady && mBsi.mPowerStatsCollectorEnabled.get(
+ BatteryConsumer.POWER_COMPONENT_CPU)) {
mBsi.mHistory.recordProcessStateChange(elapsedRealtimeMs, uptimeMs, mUid,
batteryConsumerProcessState);
}
@@ -10982,11 +11041,27 @@
public BatteryStatsImpl(@NonNull BatteryStatsConfig config, @NonNull Clock clock,
@NonNull MonotonicClock monotonicClock, @Nullable File systemDir,
- @NonNull Handler handler, @Nullable PlatformIdleStateCallback cb,
- @Nullable EnergyStatsRetriever energyStatsCb,
+ @NonNull Handler handler, @Nullable PlatformIdleStateCallback platformIdleStateCallback,
+ @Nullable EnergyStatsRetriever energyStatsRetriever,
@NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile,
@NonNull CpuScalingPolicies cpuScalingPolicies,
@NonNull PowerStatsUidResolver powerStatsUidResolver) {
+ this(config, clock, monotonicClock, systemDir, handler, platformIdleStateCallback,
+ energyStatsRetriever, userInfoProvider, powerProfile, cpuScalingPolicies,
+ powerStatsUidResolver, new FrameworkStatsLogger(),
+ new BatteryStatsHistory.TraceDelegate(), new BatteryStatsHistory.EventLogger());
+ }
+
+ public BatteryStatsImpl(@NonNull BatteryStatsConfig config, @NonNull Clock clock,
+ @NonNull MonotonicClock monotonicClock, @Nullable File systemDir,
+ @NonNull Handler handler, @Nullable PlatformIdleStateCallback platformIdleStateCallback,
+ @Nullable EnergyStatsRetriever energyStatsRetriever,
+ @NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile,
+ @NonNull CpuScalingPolicies cpuScalingPolicies,
+ @NonNull PowerStatsUidResolver powerStatsUidResolver,
+ @NonNull FrameworkStatsLogger frameworkStatsLogger,
+ @NonNull BatteryStatsHistory.TraceDelegate traceDelegate,
+ @NonNull BatteryStatsHistory.EventLogger eventLogger) {
mClock = clock;
initKernelStatsReaders();
@@ -10998,29 +11073,34 @@
mPowerProfile = powerProfile;
mCpuScalingPolicies = cpuScalingPolicies;
mPowerStatsUidResolver = powerStatsUidResolver;
- mFrameworkStatsLogger = new FrameworkStatsLogger();
+ mFrameworkStatsLogger = frameworkStatsLogger;
initPowerProfile();
- if (systemDir == null) {
- mStatsFile = null;
- mCheckinFile = null;
- mDailyFile = null;
- mHistory = new BatteryStatsHistory(mConstants.MAX_HISTORY_BUFFER,
- mStepDetailsCalculator, mClock, mMonotonicClock);
- } else {
+ if (systemDir != null) {
mStatsFile = new AtomicFile(new File(systemDir, "batterystats.bin"));
mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
- mHistory = new BatteryStatsHistory(systemDir, mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock);
+ } else {
+ mStatsFile = null;
+ mCheckinFile = null;
+ mDailyFile = null;
}
- mCpuPowerStatsCollector = new CpuPowerStatsCollector(mCpuScalingPolicies, mPowerProfile,
- mPowerStatsUidResolver, () -> mBatteryVoltageMv, mHandler,
- mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu());
+ mHistory = new BatteryStatsHistory(null /* historyBuffer */, systemDir,
+ mConstants.MAX_HISTORY_FILES, mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator,
+ mClock, mMonotonicClock, traceDelegate, eventLogger);
+
+ mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector,
+ mBatteryStatsConfig.getPowerStatsThrottlePeriod(
+ BatteryConsumer.POWER_COMPONENT_CPU));
mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
+ mMobileRadioPowerStatsCollector = new MobileRadioPowerStatsCollector(
+ mPowerStatsCollectorInjector, mBatteryStatsConfig.getPowerStatsThrottlePeriod(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO));
+ mMobileRadioPowerStatsCollector.addConsumer(this::recordPowerStats);
+
mStartCount++;
initTimersAndCounters();
mOnBattery = mOnBatteryInternal = false;
@@ -11030,8 +11110,8 @@
mStartPlatformVersion = mEndPlatformVersion = Build.ID;
initDischarge(realtimeUs);
updateDailyDeadlineLocked();
- mPlatformIdleStateCallback = cb;
- mEnergyConsumerRetriever = energyStatsCb;
+ mPlatformIdleStateCallback = platformIdleStateCallback;
+ mEnergyConsumerRetriever = energyStatsRetriever;
mUserInfoProvider = userInfoProvider;
mPowerStatsUidResolver.addListener(new PowerStatsUidResolver.Listener() {
@@ -11296,8 +11376,7 @@
memStream.writeTo(stream);
stream.flush();
mDailyFile.finishWrite(stream);
- com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
- "batterystats-daily",
+ mFrameworkStatsLogger.writeCommitSysConfigFile("batterystats-daily",
initialTimeMs + SystemClock.uptimeMillis() - startTimeMs2);
} catch (IOException e) {
Slog.w("BatteryStats",
@@ -11809,7 +11888,7 @@
// Store the empty state to disk to ensure consistency
writeSyncLocked();
- if (mPowerStatsCollectorEnabled) {
+ if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
schedulePowerStatsSampleCollection();
}
@@ -11953,7 +12032,7 @@
return networkStatsManager.getWifiUidStats();
}
- private static class NetworkStatsDelta {
+ static class NetworkStatsDelta {
int mUid;
int mSet;
long mRxBytes;
@@ -11985,9 +12064,16 @@
public long getTxPackets() {
return mTxPackets;
}
+
+ @Override
+ public String toString() {
+ return "NetworkStatsDelta{mUid=" + mUid + ", mSet=" + mSet + ", mRxBytes=" + mRxBytes
+ + ", mRxPackets=" + mRxPackets + ", mTxBytes=" + mTxBytes + ", mTxPackets="
+ + mTxPackets + '}';
+ }
}
- private List<NetworkStatsDelta> computeDelta(NetworkStats currentStats,
+ static List<NetworkStatsDelta> computeDelta(NetworkStats currentStats,
NetworkStats lastStats) {
List<NetworkStatsDelta> deltaList = new ArrayList<>();
for (NetworkStats.Entry entry : currentStats) {
@@ -12418,13 +12504,11 @@
addModemTxPowerToHistory(deltaInfo, elapsedRealtimeMs, uptimeMs);
// Grab a separate lock to acquire the network stats, which may do I/O.
- NetworkStats delta = null;
+ List<NetworkStatsDelta> delta = null;
synchronized (mModemNetworkLock) {
final NetworkStats latestStats = readMobileNetworkStatsLocked(networkStatsManager);
if (latestStats != null) {
- delta = latestStats.subtract(mLastModemNetworkStats != null
- ? mLastModemNetworkStats
- : new NetworkStats(0, -1));
+ delta = computeDelta(latestStats, mLastModemNetworkStats);
mLastModemNetworkStats = latestStats;
}
}
@@ -12527,7 +12611,7 @@
long totalRxPackets = 0;
long totalTxPackets = 0;
if (delta != null) {
- for (NetworkStats.Entry entry : delta) {
+ for (NetworkStatsDelta entry : delta) {
if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
continue;
}
@@ -12568,7 +12652,7 @@
// Now distribute proportional blame to the apps that did networking.
long totalPackets = totalRxPackets + totalTxPackets;
if (totalPackets > 0) {
- for (NetworkStats.Entry entry : delta) {
+ for (NetworkStatsDelta entry : delta) {
if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
continue;
}
@@ -14408,17 +14492,41 @@
/**
* Notifies BatteryStatsImpl that the system server is ready.
*/
- public void onSystemReady() {
+ public void onSystemReady(Context context) {
if (mCpuUidFreqTimeReader != null) {
mCpuUidFreqTimeReader.onSystemReady();
}
- if (mCpuPowerStatsCollector != null) {
- mCpuPowerStatsCollector.setEnabled(mPowerStatsCollectorEnabled);
- }
+
+ mPowerStatsCollectorInjector.setContext(context);
+
+ mCpuPowerStatsCollector.setEnabled(
+ mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU));
+ mCpuPowerStatsCollector.schedule();
+
+ mMobileRadioPowerStatsCollector.setEnabled(
+ mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO));
+ mMobileRadioPowerStatsCollector.schedule();
+
mSystemReady = true;
}
/**
+ * Returns a PowerStatsCollector for the specified power component or null if unavailable.
+ */
+ @Nullable
+ PowerStatsCollector getPowerStatsCollector(
+ @BatteryConsumer.PowerComponent int powerComponent) {
+ switch (powerComponent) {
+ case BatteryConsumer.POWER_COMPONENT_CPU:
+ return mCpuPowerStatsCollector;
+ case BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO:
+ return mMobileRadioPowerStatsCollector;
+ }
+ return null;
+ }
+
+
+ /**
* Force recording of all history events regardless of the "charging" state.
*/
@VisibleForTesting
@@ -14561,9 +14669,10 @@
stream.write(parcel.marshall());
stream.flush();
mCheckinFile.finishWrite(stream);
- com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
- "batterystats-checkin", initialTimeMs
- + SystemClock.uptimeMillis() - startTimeMs2);
+ mFrameworkStatsLogger.writeCommitSysConfigFile(
+ "batterystats-checkin",
+ initialTimeMs + SystemClock.uptimeMillis()
+ - startTimeMs2);
} catch (IOException e) {
Slog.w("BatteryStats",
"Error writing checkin battery statistics", e);
@@ -15437,9 +15546,10 @@
/**
* Enables or disables the PowerStatsCollector mode.
*/
- public void setPowerStatsCollectorEnabled(boolean enabled) {
+ public void setPowerStatsCollectorEnabled(@BatteryConsumer.PowerComponent int powerComponent,
+ boolean enabled) {
synchronized (this) {
- mPowerStatsCollectorEnabled = enabled;
+ mPowerStatsCollectorEnabled.put(powerComponent, enabled);
}
}
@@ -15944,10 +16054,8 @@
* Callers will need to wait for the collection to complete on the handler thread.
*/
public void schedulePowerStatsSampleCollection() {
- if (mCpuPowerStatsCollector == null) {
- return;
- }
mCpuPowerStatsCollector.forceSchedule();
+ mMobileRadioPowerStatsCollector.forceSchedule();
}
/**
@@ -15965,6 +16073,7 @@
*/
public void dumpStatsSample(PrintWriter pw) {
mCpuPowerStatsCollector.collectAndDump(pw);
+ mMobileRadioPowerStatsCollector.collectAndDump(pw);
}
private final Runnable mWriteAsyncRunnable = () -> {
@@ -16036,7 +16145,7 @@
+ " duration ms:" + (SystemClock.uptimeMillis() - startTimeMs)
+ " bytes:" + p.dataSize());
}
- com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
+ mFrameworkStatsLogger.writeCommitSysConfigFile(
"batterystats", SystemClock.uptimeMillis() - startTimeMs);
} catch (IOException e) {
Slog.w(TAG, "Error writing battery statistics", e);
@@ -17262,10 +17371,8 @@
pw.println();
dumpConstantsLocked(pw);
- if (mCpuPowerStatsCollector != null) {
- pw.println();
- mCpuPowerStatsCollector.dumpCpuPowerBracketsLocked(pw);
- }
+ pw.println();
+ mCpuPowerStatsCollector.dumpCpuPowerBracketsLocked(pw);
pw.println();
dumpEnergyConsumerStatsLocked(pw);
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 30b80ae..97f0986 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -27,6 +27,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import com.android.internal.os.Clock;
import com.android.internal.os.CpuScalingPolicies;
@@ -43,7 +44,7 @@
public class BatteryUsageStatsProvider {
private static final String TAG = "BatteryUsageStatsProv";
private final Context mContext;
- private boolean mPowerStatsExporterEnabled;
+ private final SparseBooleanArray mPowerStatsExporterEnabled = new SparseBooleanArray();
private final PowerStatsExporter mPowerStatsExporter;
private final PowerStatsStore mPowerStatsStore;
private final PowerProfile mPowerProfile;
@@ -71,14 +72,20 @@
// Power calculators are applied in the order of registration
mPowerCalculators.add(new BatteryChargeCalculator());
- if (!mPowerStatsExporterEnabled) {
+ if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
mPowerCalculators.add(
new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile));
}
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
if (!BatteryStats.checkWifiOnly(mContext)) {
- mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile));
+ if (!mPowerStatsExporterEnabled.get(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) {
+ mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile));
+ }
+ if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_PHONE)) {
+ mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile));
+ }
}
mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile));
mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile));
@@ -89,7 +96,6 @@
mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile));
mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile));
mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
@@ -228,7 +234,7 @@
}
}
- if (mPowerStatsExporterEnabled) {
+ if (mPowerStatsExporterEnabled.indexOfValue(true) >= 0) {
mPowerStatsExporter.exportAggregatedPowerStats(batteryUsageStatsBuilder,
monotonicStartTime, monotonicEndTime);
}
@@ -393,7 +399,10 @@
return builder.build();
}
- public void setPowerStatsExporterEnabled(boolean enabled) {
- mPowerStatsExporterEnabled = enabled;
+ /**
+ * Specify whether PowerStats based attribution is supported for the specified component.
+ */
+ public void setPowerStatsExporterEnabled(int powerComponentId, boolean enabled) {
+ mPowerStatsExporterEnabled.put(powerComponentId, enabled);
}
}
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index 1af1271..f53a1b0 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -16,14 +16,11 @@
package com.android.server.power.stats;
-import android.hardware.power.stats.EnergyConsumer;
-import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.Handler;
import android.os.PersistableBundle;
import android.os.Process;
-import android.power.PowerStatsInternal;
import android.util.Slog;
import android.util.SparseArray;
@@ -34,20 +31,11 @@
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
-import com.android.server.LocalServices;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Comparator;
-import java.util.List;
import java.util.Locale;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import java.util.function.IntSupplier;
-import java.util.function.Supplier;
/**
* Collects snapshots of power-related system statistics.
@@ -61,215 +49,55 @@
private static final long ENERGY_UNSPECIFIED = -1;
private static final int DEFAULT_CPU_POWER_BRACKETS = 3;
private static final int DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER = 2;
- private static final long POWER_STATS_ENERGY_CONSUMERS_TIMEOUT = 20000;
+
+ interface Injector {
+ Handler getHandler();
+ Clock getClock();
+ PowerStatsUidResolver getUidResolver();
+ CpuScalingPolicies getCpuScalingPolicies();
+ PowerProfile getPowerProfile();
+ KernelCpuStatsReader getKernelCpuStatsReader();
+ ConsumedEnergyRetriever getConsumedEnergyRetriever();
+ IntSupplier getVoltageSupplier();
+
+ default int getDefaultCpuPowerBrackets() {
+ return DEFAULT_CPU_POWER_BRACKETS;
+ }
+
+ default int getDefaultCpuPowerBracketsPerEnergyConsumer() {
+ return DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER;
+ }
+ }
+
+ private final Injector mInjector;
private boolean mIsInitialized;
- private final CpuScalingPolicies mCpuScalingPolicies;
- private final PowerProfile mPowerProfile;
- private final KernelCpuStatsReader mKernelCpuStatsReader;
- private final PowerStatsUidResolver mUidResolver;
- private final Supplier<PowerStatsInternal> mPowerStatsSupplier;
- private final IntSupplier mVoltageSupplier;
- private final int mDefaultCpuPowerBrackets;
- private final int mDefaultCpuPowerBracketsPerEnergyConsumer;
+ private CpuScalingPolicies mCpuScalingPolicies;
+ private PowerProfile mPowerProfile;
+ private KernelCpuStatsReader mKernelCpuStatsReader;
+ private ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ private IntSupplier mVoltageSupplier;
+ private int mDefaultCpuPowerBrackets;
+ private int mDefaultCpuPowerBracketsPerEnergyConsumer;
private long[] mCpuTimeByScalingStep;
private long[] mTempCpuTimeByScalingStep;
private long[] mTempUidStats;
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
private boolean mIsPerUidTimeInStateSupported;
- private PowerStatsInternal mPowerStatsInternal;
private int[] mCpuEnergyConsumerIds = new int[0];
private PowerStats.Descriptor mPowerStatsDescriptor;
// Reusable instance
private PowerStats mCpuPowerStats;
- private CpuStatsArrayLayout mLayout;
+ private CpuPowerStatsLayout mLayout;
private long mLastUpdateTimestampNanos;
private long mLastUpdateUptimeMillis;
private int mLastVoltageMv;
private long[] mLastConsumedEnergyUws;
- /**
- * Captures the positions and lengths of sections of the stats array, such as time-in-state,
- * power usage estimates etc.
- */
- public static class CpuStatsArrayLayout extends StatsArrayLayout {
- private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION = "dt";
- private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT = "dtc";
- private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION = "dc";
- private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc";
- private static final String EXTRA_UID_BRACKETS_POSITION = "ub";
- private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us";
-
- private int mDeviceCpuTimeByScalingStepPosition;
- private int mDeviceCpuTimeByScalingStepCount;
- private int mDeviceCpuTimeByClusterPosition;
- private int mDeviceCpuTimeByClusterCount;
-
- private int mUidPowerBracketsPosition;
- private int mUidPowerBracketCount;
-
- private int[] mScalingStepToPowerBracketMap;
-
- /**
- * Declare that the stats array has a section capturing CPU time per scaling step
- */
- public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
- mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount);
- mDeviceCpuTimeByScalingStepCount = scalingStepCount;
- }
-
- public int getCpuScalingStepCount() {
- return mDeviceCpuTimeByScalingStepCount;
- }
-
- /**
- * Saves the time duration in the <code>stats</code> element
- * corresponding to the CPU scaling <code>state</code>.
- */
- public void setTimeByScalingStep(long[] stats, int step, long value) {
- stats[mDeviceCpuTimeByScalingStepPosition + step] = value;
- }
-
- /**
- * Extracts the time duration from the <code>stats</code> element
- * corresponding to the CPU scaling <code>step</code>.
- */
- public long getTimeByScalingStep(long[] stats, int step) {
- return stats[mDeviceCpuTimeByScalingStepPosition + step];
- }
-
- /**
- * Declare that the stats array has a section capturing CPU time in each cluster
- */
- public void addDeviceSectionCpuTimeByCluster(int clusterCount) {
- mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount);
- mDeviceCpuTimeByClusterCount = clusterCount;
- }
-
- public int getCpuClusterCount() {
- return mDeviceCpuTimeByClusterCount;
- }
-
- /**
- * Saves the time duration in the <code>stats</code> element
- * corresponding to the CPU <code>cluster</code>.
- */
- public void setTimeByCluster(long[] stats, int cluster, long value) {
- stats[mDeviceCpuTimeByClusterPosition + cluster] = value;
- }
-
- /**
- * Extracts the time duration from the <code>stats</code> element
- * corresponding to the CPU <code>cluster</code>.
- */
- public long getTimeByCluster(long[] stats, int cluster) {
- return stats[mDeviceCpuTimeByClusterPosition + cluster];
- }
-
- /**
- * Declare that the UID stats array has a section capturing CPU time per power bracket.
- */
- public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
- mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
- updatePowerBracketCount();
- mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount);
- }
-
- private void updatePowerBracketCount() {
- mUidPowerBracketCount = 1;
- for (int bracket : mScalingStepToPowerBracketMap) {
- if (bracket >= mUidPowerBracketCount) {
- mUidPowerBracketCount = bracket + 1;
- }
- }
- }
-
- public int[] getScalingStepToPowerBracketMap() {
- return mScalingStepToPowerBracketMap;
- }
-
- public int getCpuPowerBracketCount() {
- return mUidPowerBracketCount;
- }
-
- /**
- * Saves time in <code>bracket</code> in the corresponding section of <code>stats</code>.
- */
- public void setUidTimeByPowerBracket(long[] stats, int bracket, long value) {
- stats[mUidPowerBracketsPosition + bracket] = value;
- }
-
- /**
- * Extracts the time in <code>bracket</code> from a UID stats array.
- */
- public long getUidTimeByPowerBracket(long[] stats, int bracket) {
- return stats[mUidPowerBracketsPosition + bracket];
- }
-
- /**
- * Copies the elements of the stats array layout into <code>extras</code>
- */
- public void toExtras(PersistableBundle extras) {
- super.toExtras(extras);
- extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION,
- mDeviceCpuTimeByScalingStepPosition);
- extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT,
- mDeviceCpuTimeByScalingStepCount);
- extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION,
- mDeviceCpuTimeByClusterPosition);
- extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT,
- mDeviceCpuTimeByClusterCount);
- extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition);
- putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
- mScalingStepToPowerBracketMap);
- }
-
- /**
- * Retrieves elements of the stats array layout from <code>extras</code>
- */
- public void fromExtras(PersistableBundle extras) {
- super.fromExtras(extras);
- mDeviceCpuTimeByScalingStepPosition =
- extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION);
- mDeviceCpuTimeByScalingStepCount =
- extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT);
- mDeviceCpuTimeByClusterPosition =
- extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION);
- mDeviceCpuTimeByClusterCount =
- extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT);
- mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION);
- mScalingStepToPowerBracketMap =
- getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
- if (mScalingStepToPowerBracketMap == null) {
- mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount];
- }
- updatePowerBracketCount();
- }
- }
-
- public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
- PowerStatsUidResolver uidResolver, IntSupplier voltageSupplier, Handler handler,
- long throttlePeriodMs) {
- this(cpuScalingPolicies, powerProfile, handler, new KernelCpuStatsReader(), uidResolver,
- () -> LocalServices.getService(PowerStatsInternal.class), voltageSupplier,
- throttlePeriodMs, Clock.SYSTEM_CLOCK, DEFAULT_CPU_POWER_BRACKETS,
- DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER);
- }
-
- public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
- Handler handler, KernelCpuStatsReader kernelCpuStatsReader,
- PowerStatsUidResolver uidResolver, Supplier<PowerStatsInternal> powerStatsSupplier,
- IntSupplier voltageSupplier, long throttlePeriodMs, Clock clock,
- int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) {
- super(handler, throttlePeriodMs, clock);
- mCpuScalingPolicies = cpuScalingPolicies;
- mPowerProfile = powerProfile;
- mKernelCpuStatsReader = kernelCpuStatsReader;
- mUidResolver = uidResolver;
- mPowerStatsSupplier = powerStatsSupplier;
- mVoltageSupplier = voltageSupplier;
- mDefaultCpuPowerBrackets = defaultCpuPowerBrackets;
- mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer;
+ public CpuPowerStatsCollector(Injector injector, long throttlePeriodMs) {
+ super(injector.getHandler(), throttlePeriodMs, injector.getUidResolver(),
+ injector.getClock());
+ mInjector = injector;
}
private boolean ensureInitialized() {
@@ -281,19 +109,27 @@
return false;
}
- mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.nativeIsSupportedFeature();
- mPowerStatsInternal = mPowerStatsSupplier.get();
+ mCpuScalingPolicies = mInjector.getCpuScalingPolicies();
+ mPowerProfile = mInjector.getPowerProfile();
+ mKernelCpuStatsReader = mInjector.getKernelCpuStatsReader();
+ mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
+ mVoltageSupplier = mInjector.getVoltageSupplier();
+ mDefaultCpuPowerBrackets = mInjector.getDefaultCpuPowerBrackets();
+ mDefaultCpuPowerBracketsPerEnergyConsumer =
+ mInjector.getDefaultCpuPowerBracketsPerEnergyConsumer();
- if (mPowerStatsInternal != null) {
- readCpuEnergyConsumerIds();
- }
+ mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.isSupportedFeature();
+ mCpuEnergyConsumerIds =
+ mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER);
+ mLastConsumedEnergyUws = new long[mCpuEnergyConsumerIds.length];
+ Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
int cpuScalingStepCount = mCpuScalingPolicies.getScalingStepCount();
mCpuTimeByScalingStep = new long[cpuScalingStepCount];
mTempCpuTimeByScalingStep = new long[cpuScalingStepCount];
int[] scalingStepToPowerBracketMap = initPowerBrackets();
- mLayout = new CpuStatsArrayLayout();
+ mLayout = new CpuPowerStatsLayout();
mLayout.addDeviceSectionCpuTimeByScalingStep(cpuScalingStepCount);
mLayout.addDeviceSectionCpuTimeByCluster(mCpuScalingPolicies.getPolicies().length);
mLayout.addDeviceSectionUsageDuration();
@@ -306,7 +142,8 @@
mLayout.toExtras(extras);
mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
- mLayout.getDeviceStatsArrayLength(), mLayout.getUidStatsArrayLength(), extras);
+ mLayout.getDeviceStatsArrayLength(), /* stateLabels */null,
+ /* stateStatsArrayLength */ 0, mLayout.getUidStatsArrayLength(), extras);
mCpuPowerStats = new PowerStats(mPowerStatsDescriptor);
mTempUidStats = new long[mLayout.getCpuPowerBracketCount()];
@@ -315,32 +152,6 @@
return true;
}
- private void readCpuEnergyConsumerIds() {
- EnergyConsumer[] energyConsumerInfo = mPowerStatsInternal.getEnergyConsumerInfo();
- if (energyConsumerInfo == null) {
- return;
- }
-
- List<EnergyConsumer> cpuEnergyConsumers = new ArrayList<>();
- for (EnergyConsumer energyConsumer : energyConsumerInfo) {
- if (energyConsumer.type == EnergyConsumerType.CPU_CLUSTER) {
- cpuEnergyConsumers.add(energyConsumer);
- }
- }
- if (cpuEnergyConsumers.isEmpty()) {
- return;
- }
-
- cpuEnergyConsumers.sort(Comparator.comparing(c -> c.ordinal));
-
- mCpuEnergyConsumerIds = new int[cpuEnergyConsumers.size()];
- for (int i = 0; i < mCpuEnergyConsumerIds.length; i++) {
- mCpuEnergyConsumerIds[i] = cpuEnergyConsumers.get(i).id;
- }
- mLastConsumedEnergyUws = new long[cpuEnergyConsumers.size()];
- Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
- }
-
private int[] initPowerBrackets() {
if (mPowerProfile.getCpuPowerBracketCount() != PowerProfile.POWER_BRACKETS_UNSPECIFIED) {
return initPowerBracketsFromPowerProfile();
@@ -372,6 +183,7 @@
return stepToBracketMap;
}
+
private int[] initPowerBracketsByCluster(int defaultBracketCountPerCluster) {
int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()];
int index = 0;
@@ -531,7 +343,7 @@
mCpuPowerStats.uidStats.clear();
// TODO(b/305120724): additionally retrieve time-in-cluster for each CPU cluster
- long newTimestampNanos = mKernelCpuStatsReader.nativeReadCpuStats(this::processUidStats,
+ long newTimestampNanos = mKernelCpuStatsReader.readCpuStats(this::processUidStats,
mLayout.getScalingStepToPowerBracketMap(), mLastUpdateTimestampNanos,
mTempCpuTimeByScalingStep, mTempUidStats);
for (int step = mLayout.getCpuScalingStepCount() - 1; step >= 0; step--) {
@@ -571,35 +383,20 @@
int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
mLastVoltageMv = voltageMv;
- CompletableFuture<EnergyConsumerResult[]> future =
- mPowerStatsInternal.getEnergyConsumedAsync(mCpuEnergyConsumerIds);
- EnergyConsumerResult[] results = null;
- try {
- results = future.get(
- POWER_STATS_ENERGY_CONSUMERS_TIMEOUT, TimeUnit.MILLISECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- Slog.e(TAG, "Could not obtain energy consumers from PowerStatsService", e);
- }
- if (results == null) {
+ long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mCpuEnergyConsumerIds);
+ if (energyUws == null) {
return;
}
- for (int i = 0; i < mCpuEnergyConsumerIds.length; i++) {
- int id = mCpuEnergyConsumerIds[i];
- for (EnergyConsumerResult result : results) {
- if (result.id == id) {
- long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
- ? result.energyUWs - mLastConsumedEnergyUws[i] : 0;
- if (energyDelta < 0) {
- // Likely, restart of powerstats HAL
- energyDelta = 0;
- }
- mLayout.setConsumedEnergy(mCpuPowerStats.stats, i,
- uJtoUc(energyDelta, averageVoltage));
- mLastConsumedEnergyUws[i] = result.energyUWs;
- break;
- }
+ for (int i = energyUws.length - 1; i >= 0; i--) {
+ long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
+ ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
+ if (energyDelta < 0) {
+ // Likely, restart of powerstats HAL
+ energyDelta = 0;
}
+ mLayout.setConsumedEnergy(mCpuPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
+ mLastConsumedEnergyUws[i] = energyUws[i];
}
}
@@ -622,7 +419,8 @@
boolean nonzero = false;
for (int bracket = powerBracketCount - 1; bracket >= 0; bracket--) {
- long delta = timeByPowerBracket[bracket] - uidStats.timeByPowerBracket[bracket];
+ long delta = Math.max(0,
+ timeByPowerBracket[bracket] - uidStats.timeByPowerBracket[bracket]);
if (delta != 0) {
nonzero = true;
}
@@ -648,10 +446,27 @@
}
}
+ @Override
+ protected void onUidRemoved(int uid) {
+ super.onUidRemoved(uid);
+ mUidStats.remove(uid);
+ }
+
/**
* Native class that retrieves CPU stats from the kernel.
*/
public static class KernelCpuStatsReader {
+ protected boolean isSupportedFeature() {
+ return nativeIsSupportedFeature();
+ }
+
+ protected long readCpuStats(KernelCpuStatsCallback callback,
+ int[] scalingStepToPowerBracketMap, long lastUpdateTimestampNanos,
+ long[] outCpuTimeByScalingStep, long[] tempForUidStats) {
+ return nativeReadCpuStats(callback, scalingStepToPowerBracketMap,
+ lastUpdateTimestampNanos, outCpuTimeByScalingStep, tempForUidStats);
+ }
+
protected native boolean nativeIsSupportedFeature();
protected native long nativeReadCpuStats(KernelCpuStatsCallback callback,
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
new file mode 100644
index 0000000..1bcb2c4
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.os.PersistableBundle;
+
+/**
+ * Captures the positions and lengths of sections of the stats array, such as time-in-state,
+ * power usage estimates etc.
+ */
+public class CpuPowerStatsLayout extends PowerStatsLayout {
+ private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION = "dt";
+ private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT = "dtc";
+ private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION = "dc";
+ private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc";
+ private static final String EXTRA_UID_BRACKETS_POSITION = "ub";
+ private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us";
+
+ private int mDeviceCpuTimeByScalingStepPosition;
+ private int mDeviceCpuTimeByScalingStepCount;
+ private int mDeviceCpuTimeByClusterPosition;
+ private int mDeviceCpuTimeByClusterCount;
+
+ private int mUidPowerBracketsPosition;
+ private int mUidPowerBracketCount;
+
+ private int[] mScalingStepToPowerBracketMap;
+
+ /**
+ * Declare that the stats array has a section capturing CPU time per scaling step
+ */
+ public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
+ mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount);
+ mDeviceCpuTimeByScalingStepCount = scalingStepCount;
+ }
+
+ public int getCpuScalingStepCount() {
+ return mDeviceCpuTimeByScalingStepCount;
+ }
+
+ /**
+ * Saves the time duration in the <code>stats</code> element
+ * corresponding to the CPU scaling <code>state</code>.
+ */
+ public void setTimeByScalingStep(long[] stats, int step, long value) {
+ stats[mDeviceCpuTimeByScalingStepPosition + step] = value;
+ }
+
+ /**
+ * Extracts the time duration from the <code>stats</code> element
+ * corresponding to the CPU scaling <code>step</code>.
+ */
+ public long getTimeByScalingStep(long[] stats, int step) {
+ return stats[mDeviceCpuTimeByScalingStepPosition + step];
+ }
+
+ /**
+ * Declare that the stats array has a section capturing CPU time in each cluster
+ */
+ public void addDeviceSectionCpuTimeByCluster(int clusterCount) {
+ mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount);
+ mDeviceCpuTimeByClusterCount = clusterCount;
+ }
+
+ public int getCpuClusterCount() {
+ return mDeviceCpuTimeByClusterCount;
+ }
+
+ /**
+ * Saves the time duration in the <code>stats</code> element
+ * corresponding to the CPU <code>cluster</code>.
+ */
+ public void setTimeByCluster(long[] stats, int cluster, long value) {
+ stats[mDeviceCpuTimeByClusterPosition + cluster] = value;
+ }
+
+ /**
+ * Extracts the time duration from the <code>stats</code> element
+ * corresponding to the CPU <code>cluster</code>.
+ */
+ public long getTimeByCluster(long[] stats, int cluster) {
+ return stats[mDeviceCpuTimeByClusterPosition + cluster];
+ }
+
+ /**
+ * Declare that the UID stats array has a section capturing CPU time per power bracket.
+ */
+ public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
+ mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
+ updatePowerBracketCount();
+ mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount);
+ }
+
+ private void updatePowerBracketCount() {
+ mUidPowerBracketCount = 1;
+ for (int bracket : mScalingStepToPowerBracketMap) {
+ if (bracket >= mUidPowerBracketCount) {
+ mUidPowerBracketCount = bracket + 1;
+ }
+ }
+ }
+
+ public int[] getScalingStepToPowerBracketMap() {
+ return mScalingStepToPowerBracketMap;
+ }
+
+ public int getCpuPowerBracketCount() {
+ return mUidPowerBracketCount;
+ }
+
+ /**
+ * Saves time in <code>bracket</code> in the corresponding section of <code>stats</code>.
+ */
+ public void setUidTimeByPowerBracket(long[] stats, int bracket, long value) {
+ stats[mUidPowerBracketsPosition + bracket] = value;
+ }
+
+ /**
+ * Extracts the time in <code>bracket</code> from a UID stats array.
+ */
+ public long getUidTimeByPowerBracket(long[] stats, int bracket) {
+ return stats[mUidPowerBracketsPosition + bracket];
+ }
+
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
+ extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION,
+ mDeviceCpuTimeByScalingStepPosition);
+ extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT,
+ mDeviceCpuTimeByScalingStepCount);
+ extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION,
+ mDeviceCpuTimeByClusterPosition);
+ extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT,
+ mDeviceCpuTimeByClusterCount);
+ extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition);
+ putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
+ mScalingStepToPowerBracketMap);
+ }
+
+ /**
+ * Retrieves elements of the stats array layout from <code>extras</code>
+ */
+ public void fromExtras(PersistableBundle extras) {
+ super.fromExtras(extras);
+ mDeviceCpuTimeByScalingStepPosition =
+ extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION);
+ mDeviceCpuTimeByScalingStepCount =
+ extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT);
+ mDeviceCpuTimeByClusterPosition =
+ extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION);
+ mDeviceCpuTimeByClusterCount =
+ extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT);
+ mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION);
+ mScalingStepToPowerBracketMap =
+ getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
+ if (mScalingStepToPowerBracketMap == null) {
+ mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount];
+ }
+ updatePowerBracketCount();
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
similarity index 97%
rename from services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
index ed9414f..c34b8a8 100644
--- a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
@@ -29,8 +29,8 @@
import java.util.List;
import java.util.concurrent.TimeUnit;
-public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProcessor {
- private static final String TAG = "CpuAggregatedPowerStatsProcessor";
+public class CpuPowerStatsProcessor extends PowerStatsProcessor {
+ private static final String TAG = "CpuPowerStatsProcessor";
private static final double HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1);
private static final int UNKNOWN = -1;
@@ -64,7 +64,7 @@
private PowerStats.Descriptor mLastUsedDescriptor;
// Cached results of parsing of current PowerStats.Descriptor. Only refreshed when
// mLastUsedDescriptor changes
- private CpuPowerStatsCollector.CpuStatsArrayLayout mStatsLayout;
+ private CpuPowerStatsLayout mStatsLayout;
// Sequence of steps for power estimation and intermediate results.
private PowerEstimationPlan mPlan;
@@ -73,8 +73,7 @@
// Temp array for retrieval of UID power stats, to avoid repeated allocations
private long[] mTmpUidStatsArray;
- public CpuAggregatedPowerStatsProcessor(PowerProfile powerProfile,
- CpuScalingPolicies scalingPolicies) {
+ public CpuPowerStatsProcessor(PowerProfile powerProfile, CpuScalingPolicies scalingPolicies) {
mCpuScalingPolicies = scalingPolicies;
mCpuScalingStepCount = scalingPolicies.getScalingStepCount();
mScalingStepToCluster = new int[mCpuScalingStepCount];
@@ -106,7 +105,7 @@
}
mLastUsedDescriptor = descriptor;
- mStatsLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ mStatsLayout = new CpuPowerStatsLayout();
mStatsLayout.fromExtras(descriptor.extras);
mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
@@ -527,6 +526,12 @@
}
@Override
+ String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
+ // Unsupported for this power component
+ return null;
+ }
+
+ @Override
public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
unpackPowerStatsDescriptor(descriptor);
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
index 9ea143e..c01363a9 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
@@ -387,92 +387,14 @@
return consumptionMah;
}
- private static long buildModemPowerProfileKey(@ModemPowerProfile.ModemDrainType int drainType,
- @BatteryStats.RadioAccessTechnology int rat, @ServiceState.FrequencyRange int freqRange,
- int txLevel) {
- long key = PowerProfile.SUBSYSTEM_MODEM;
-
- // Attach Modem drain type to the key if specified.
- if (drainType != IGNORE) {
- key |= drainType;
- }
-
- // Attach RadioAccessTechnology to the key if specified.
- switch (rat) {
- case IGNORE:
- // do nothing
- break;
- case BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER:
- key |= ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT;
- break;
- case BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE:
- key |= ModemPowerProfile.MODEM_RAT_TYPE_LTE;
- break;
- case BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR:
- key |= ModemPowerProfile.MODEM_RAT_TYPE_NR;
- break;
- default:
- Log.w(TAG, "Unexpected RadioAccessTechnology : " + rat);
- }
-
- // Attach NR Frequency Range to the key if specified.
- switch (freqRange) {
- case IGNORE:
- // do nothing
- break;
- case ServiceState.FREQUENCY_RANGE_UNKNOWN:
- key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT;
- break;
- case ServiceState.FREQUENCY_RANGE_LOW:
- key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW;
- break;
- case ServiceState.FREQUENCY_RANGE_MID:
- key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID;
- break;
- case ServiceState.FREQUENCY_RANGE_HIGH:
- key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH;
- break;
- case ServiceState.FREQUENCY_RANGE_MMWAVE:
- key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE;
- break;
- default:
- Log.w(TAG, "Unexpected NR frequency range : " + freqRange);
- }
-
- // Attach transmission level to the key if specified.
- switch (txLevel) {
- case IGNORE:
- // do nothing
- break;
- case 0:
- key |= ModemPowerProfile.MODEM_TX_LEVEL_0;
- break;
- case 1:
- key |= ModemPowerProfile.MODEM_TX_LEVEL_1;
- break;
- case 2:
- key |= ModemPowerProfile.MODEM_TX_LEVEL_2;
- break;
- case 3:
- key |= ModemPowerProfile.MODEM_TX_LEVEL_3;
- break;
- case 4:
- key |= ModemPowerProfile.MODEM_TX_LEVEL_4;
- break;
- default:
- Log.w(TAG, "Unexpected transmission level : " + txLevel);
- }
- return key;
- }
-
/**
* Calculates active receive radio power consumption (in milliamp-hours) from the given state's
* duration.
*/
public double calcRxStatePowerMah(@BatteryStats.RadioAccessTechnology int rat,
@ServiceState.FrequencyRange int freqRange, long rxDurationMs) {
- final long rxKey = buildModemPowerProfileKey(ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat,
- freqRange, IGNORE);
+ final long rxKey = ModemPowerProfile.getAverageBatteryDrainKey(
+ ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat, freqRange, IGNORE);
final double drainRateMa = mPowerProfile.getAverageBatteryDrainOrDefaultMa(rxKey,
Double.NaN);
if (Double.isNaN(drainRateMa)) {
@@ -495,8 +417,8 @@
*/
public double calcTxStatePowerMah(@BatteryStats.RadioAccessTechnology int rat,
@ServiceState.FrequencyRange int freqRange, int txLevel, long txDurationMs) {
- final long txKey = buildModemPowerProfileKey(ModemPowerProfile.MODEM_DRAIN_TYPE_TX, rat,
- freqRange, txLevel);
+ final long txKey = ModemPowerProfile.getAverageBatteryDrainKey(
+ ModemPowerProfile.MODEM_DRAIN_TYPE_TX, rat, freqRange, txLevel);
final double drainRateMa = mPowerProfile.getAverageBatteryDrainOrDefaultMa(txKey,
Double.NaN);
if (Double.isNaN(drainRateMa)) {
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
new file mode 100644
index 0000000..7bc6817
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.OutcomeReceiver;
+import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+public class MobileRadioPowerStatsCollector extends PowerStatsCollector {
+ private static final String TAG = "MobileRadioPowerStatsCollector";
+
+ /**
+ * The soonest the Mobile Radio stats can be updated due to a mobile radio power state change
+ * after it was last updated.
+ */
+ @VisibleForTesting
+ protected static final long MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS = 1000 * 60 * 10;
+
+ private static final long MODEM_ACTIVITY_REQUEST_TIMEOUT = 20000;
+
+ private static final long ENERGY_UNSPECIFIED = -1;
+
+ @VisibleForTesting
+ @AccessNetworkConstants.RadioAccessNetworkType
+ static final int[] NETWORK_TYPES = {
+ AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ AccessNetworkConstants.AccessNetworkType.UTRAN,
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ AccessNetworkConstants.AccessNetworkType.CDMA2000,
+ AccessNetworkConstants.AccessNetworkType.IWLAN,
+ AccessNetworkConstants.AccessNetworkType.NGRAN
+ };
+
+ interface Injector {
+ Handler getHandler();
+ Clock getClock();
+ PowerStatsUidResolver getUidResolver();
+ PackageManager getPackageManager();
+ ConsumedEnergyRetriever getConsumedEnergyRetriever();
+ IntSupplier getVoltageSupplier();
+ Supplier<NetworkStats> getMobileNetworkStatsSupplier();
+ TelephonyManager getTelephonyManager();
+ LongSupplier getCallDurationSupplier();
+ LongSupplier getPhoneSignalScanDurationSupplier();
+ }
+
+ private final Injector mInjector;
+
+ private MobileRadioPowerStatsLayout mLayout;
+ private boolean mIsInitialized;
+
+ private PowerStats mPowerStats;
+ private long[] mDeviceStats;
+ private volatile TelephonyManager mTelephonyManager;
+ private LongSupplier mCallDurationSupplier;
+ private LongSupplier mScanDurationSupplier;
+ private volatile Supplier<NetworkStats> mNetworkStatsSupplier;
+ private ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ private IntSupplier mVoltageSupplier;
+ private int[] mEnergyConsumerIds = new int[0];
+ private long mLastUpdateTimestampMillis;
+ private ModemActivityInfo mLastModemActivityInfo;
+ private NetworkStats mLastNetworkStats;
+ private long[] mLastConsumedEnergyUws;
+ private int mLastVoltageMv;
+ private long mLastCallDuration;
+ private long mLastScanDuration;
+
+ public MobileRadioPowerStatsCollector(Injector injector, long throttlePeriodMs) {
+ super(injector.getHandler(), throttlePeriodMs, injector.getUidResolver(),
+ injector.getClock());
+ mInjector = injector;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (enabled) {
+ PackageManager packageManager = mInjector.getPackageManager();
+ super.setEnabled(packageManager != null
+ && packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
+ } else {
+ super.setEnabled(false);
+ }
+ }
+
+ private boolean ensureInitialized() {
+ if (mIsInitialized) {
+ return true;
+ }
+
+ if (!isEnabled()) {
+ return false;
+ }
+
+ mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
+ mVoltageSupplier = mInjector.getVoltageSupplier();
+
+ mTelephonyManager = mInjector.getTelephonyManager();
+ mNetworkStatsSupplier = mInjector.getMobileNetworkStatsSupplier();
+ mCallDurationSupplier = mInjector.getCallDurationSupplier();
+ mScanDurationSupplier = mInjector.getPhoneSignalScanDurationSupplier();
+
+ mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(
+ EnergyConsumerType.MOBILE_RADIO);
+ mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
+ Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
+
+ mLayout = new MobileRadioPowerStatsLayout();
+ mLayout.addDeviceMobileActivity();
+ mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
+ mLayout.addStateStats();
+ mLayout.addUidNetworkStats();
+ mLayout.addDeviceSectionUsageDuration();
+ mLayout.addDeviceSectionPowerEstimate();
+ mLayout.addUidSectionPowerEstimate();
+
+ SparseArray<String> stateLabels = new SparseArray<>();
+ for (int rat = 0; rat < BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
+ final int freqCount = rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR
+ ? ServiceState.FREQUENCY_RANGE_COUNT : 1;
+ for (int freq = 0; freq < freqCount; freq++) {
+ int stateKey = makeStateKey(rat, freq);
+ StringBuilder sb = new StringBuilder();
+ if (rat != BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER) {
+ sb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]);
+ }
+ if (freq != ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+ if (!sb.isEmpty()) {
+ sb.append(" ");
+ }
+ sb.append(ServiceState.frequencyRangeToString(freq));
+ }
+ stateLabels.put(stateKey, !sb.isEmpty() ? sb.toString() : "other");
+ }
+ }
+
+ PersistableBundle extras = new PersistableBundle();
+ mLayout.toExtras(extras);
+ PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, mLayout.getDeviceStatsArrayLength(),
+ stateLabels, mLayout.getStateStatsArrayLength(), mLayout.getUidStatsArrayLength(),
+ extras);
+ mPowerStats = new PowerStats(powerStatsDescriptor);
+ mDeviceStats = mPowerStats.stats;
+
+ mIsInitialized = true;
+ return true;
+ }
+
+ @Override
+ protected PowerStats collectStats() {
+ if (!ensureInitialized()) {
+ return null;
+ }
+
+ collectModemActivityInfo();
+
+ collectNetworkStats();
+
+ if (mEnergyConsumerIds.length != 0) {
+ collectEnergyConsumers();
+ }
+
+ if (mPowerStats.durationMs == 0) {
+ setTimestamp(mClock.elapsedRealtime());
+ }
+
+ return mPowerStats;
+ }
+
+ private void collectModemActivityInfo() {
+ if (mTelephonyManager == null) {
+ return;
+ }
+
+ CompletableFuture<ModemActivityInfo> immediateFuture = new CompletableFuture<>();
+ mTelephonyManager.requestModemActivityInfo(Runnable::run,
+ new OutcomeReceiver<>() {
+ @Override
+ public void onResult(ModemActivityInfo result) {
+ immediateFuture.complete(result);
+ }
+
+ @Override
+ public void onError(TelephonyManager.ModemActivityInfoException e) {
+ Slog.w(TAG, "error reading modem stats:" + e);
+ immediateFuture.complete(null);
+ }
+ });
+
+ ModemActivityInfo activityInfo;
+ try {
+ activityInfo = immediateFuture.get(MODEM_ACTIVITY_REQUEST_TIMEOUT,
+ TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot acquire ModemActivityInfo");
+ activityInfo = null;
+ }
+
+ ModemActivityInfo deltaInfo = mLastModemActivityInfo == null
+ ? (activityInfo == null ? null : activityInfo.getDelta(activityInfo))
+ : mLastModemActivityInfo.getDelta(activityInfo);
+
+ mLastModemActivityInfo = activityInfo;
+
+ if (deltaInfo == null) {
+ return;
+ }
+
+ setTimestamp(deltaInfo.getTimestampMillis());
+ mLayout.setDeviceSleepTime(mDeviceStats, deltaInfo.getSleepTimeMillis());
+ mLayout.setDeviceIdleTime(mDeviceStats, deltaInfo.getIdleTimeMillis());
+
+ long callDuration = mCallDurationSupplier.getAsLong();
+ if (callDuration >= mLastCallDuration) {
+ mLayout.setDeviceCallTime(mDeviceStats, callDuration - mLastCallDuration);
+ }
+ mLastCallDuration = callDuration;
+
+ long scanDuration = mScanDurationSupplier.getAsLong();
+ if (scanDuration >= mLastScanDuration) {
+ mLayout.setDeviceScanTime(mDeviceStats, scanDuration - mLastScanDuration);
+ }
+ mLastScanDuration = scanDuration;
+
+ SparseArray<long[]> stateStats = mPowerStats.stateStats;
+ stateStats.clear();
+
+ if (deltaInfo.getSpecificInfoLength() == 0) {
+ mLayout.addRxTxTimesForRat(stateStats,
+ AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN,
+ deltaInfo.getReceiveTimeMillis(),
+ deltaInfo.getTransmitTimeMillis());
+ } else {
+ for (int rat = 0; rat < NETWORK_TYPES.length; rat++) {
+ if (rat == AccessNetworkConstants.AccessNetworkType.NGRAN) {
+ for (int freq = 0; freq < ServiceState.FREQUENCY_RANGE_COUNT; freq++) {
+ mLayout.addRxTxTimesForRat(stateStats, rat, freq,
+ deltaInfo.getReceiveTimeMillis(rat, freq),
+ deltaInfo.getTransmitTimeMillis(rat, freq));
+ }
+ } else {
+ mLayout.addRxTxTimesForRat(stateStats, rat,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN,
+ deltaInfo.getReceiveTimeMillis(rat),
+ deltaInfo.getTransmitTimeMillis(rat));
+ }
+ }
+ }
+ }
+
+ private void collectNetworkStats() {
+ mPowerStats.uidStats.clear();
+
+ NetworkStats networkStats = mNetworkStatsSupplier.get();
+ if (networkStats == null) {
+ return;
+ }
+
+ List<BatteryStatsImpl.NetworkStatsDelta> delta =
+ BatteryStatsImpl.computeDelta(networkStats, mLastNetworkStats);
+ mLastNetworkStats = networkStats;
+ for (int i = delta.size() - 1; i >= 0; i--) {
+ BatteryStatsImpl.NetworkStatsDelta uidDelta = delta.get(i);
+ long rxBytes = uidDelta.getRxBytes();
+ long txBytes = uidDelta.getTxBytes();
+ long rxPackets = uidDelta.getRxPackets();
+ long txPackets = uidDelta.getTxPackets();
+ if (rxBytes == 0 && txBytes == 0 && rxPackets == 0 && txPackets == 0) {
+ continue;
+ }
+
+ int uid = mUidResolver.mapUid(uidDelta.getUid());
+ long[] stats = mPowerStats.uidStats.get(uid);
+ if (stats == null) {
+ stats = new long[mLayout.getUidStatsArrayLength()];
+ mPowerStats.uidStats.put(uid, stats);
+ mLayout.setUidRxBytes(stats, rxBytes);
+ mLayout.setUidTxBytes(stats, txBytes);
+ mLayout.setUidRxPackets(stats, rxPackets);
+ mLayout.setUidTxPackets(stats, txPackets);
+ } else {
+ mLayout.setUidRxBytes(stats, mLayout.getUidRxBytes(stats) + rxBytes);
+ mLayout.setUidTxBytes(stats, mLayout.getUidTxBytes(stats) + txBytes);
+ mLayout.setUidRxPackets(stats, mLayout.getUidRxPackets(stats) + rxPackets);
+ mLayout.setUidTxPackets(stats, mLayout.getUidTxPackets(stats) + txPackets);
+ }
+ }
+ }
+
+ private void collectEnergyConsumers() {
+ int voltageMv = mVoltageSupplier.getAsInt();
+ if (voltageMv <= 0) {
+ Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
+ + " mV) when querying energy consumers");
+ return;
+ }
+
+ int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
+ mLastVoltageMv = voltageMv;
+
+ long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
+ if (energyUws == null) {
+ return;
+ }
+
+ for (int i = energyUws.length - 1; i >= 0; i--) {
+ long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
+ ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
+ if (energyDelta < 0) {
+ // Likely, restart of powerstats HAL
+ energyDelta = 0;
+ }
+ mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
+ mLastConsumedEnergyUws[i] = energyUws[i];
+ }
+ }
+
+ static int makeStateKey(int rat, int freqRange) {
+ if (rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR) {
+ return rat | (freqRange << 8);
+ } else {
+ return rat;
+ }
+ }
+
+ private void setTimestamp(long timestamp) {
+ mPowerStats.durationMs = Math.max(timestamp - mLastUpdateTimestampMillis, 0);
+ mLastUpdateTimestampMillis = timestamp;
+ }
+
+ @BatteryStats.RadioAccessTechnology
+ static int mapRadioAccessNetworkTypeToRadioAccessTechnology(
+ @AccessNetworkConstants.RadioAccessNetworkType int networkType) {
+ switch (networkType) {
+ case AccessNetworkConstants.AccessNetworkType.NGRAN:
+ return BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR;
+ case AccessNetworkConstants.AccessNetworkType.EUTRAN:
+ return BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE;
+ case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.IWLAN:
+ return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
+ default:
+ Slog.w(TAG,
+ "Unhandled RadioAccessNetworkType (" + networkType + "), mapping to OTHER");
+ return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
new file mode 100644
index 0000000..81d7c2f
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+import android.telephony.ModemActivityInfo;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.os.PowerStats;
+
+/**
+ * Captures the positions and lengths of sections of the stats array, such as time-in-state,
+ * power usage estimates etc.
+ */
+class MobileRadioPowerStatsLayout extends PowerStatsLayout {
+ private static final String TAG = "MobileRadioPowerStatsLayout";
+ private static final String EXTRA_DEVICE_SLEEP_TIME_POSITION = "dt-sleep";
+ private static final String EXTRA_DEVICE_IDLE_TIME_POSITION = "dt-idle";
+ private static final String EXTRA_DEVICE_SCAN_TIME_POSITION = "dt-scan";
+ private static final String EXTRA_DEVICE_CALL_TIME_POSITION = "dt-call";
+ private static final String EXTRA_DEVICE_CALL_POWER_POSITION = "dp-call";
+ private static final String EXTRA_STATE_RX_TIME_POSITION = "srx";
+ private static final String EXTRA_STATE_TX_TIMES_POSITION = "stx";
+ private static final String EXTRA_STATE_TX_TIMES_COUNT = "stxc";
+ private static final String EXTRA_UID_RX_BYTES_POSITION = "urxb";
+ private static final String EXTRA_UID_TX_BYTES_POSITION = "utxb";
+ private static final String EXTRA_UID_RX_PACKETS_POSITION = "urxp";
+ private static final String EXTRA_UID_TX_PACKETS_POSITION = "utxp";
+
+ private int mDeviceSleepTimePosition;
+ private int mDeviceIdleTimePosition;
+ private int mDeviceScanTimePosition;
+ private int mDeviceCallTimePosition;
+ private int mDeviceCallPowerPosition;
+ private int mStateRxTimePosition;
+ private int mStateTxTimesPosition;
+ private int mStateTxTimesCount;
+ private int mUidRxBytesPosition;
+ private int mUidTxBytesPosition;
+ private int mUidRxPacketsPosition;
+ private int mUidTxPacketsPosition;
+
+ MobileRadioPowerStatsLayout() {
+ }
+
+ MobileRadioPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+ super(descriptor);
+ }
+
+ void addDeviceMobileActivity() {
+ mDeviceSleepTimePosition = addDeviceSection(1);
+ mDeviceIdleTimePosition = addDeviceSection(1);
+ mDeviceScanTimePosition = addDeviceSection(1);
+ mDeviceCallTimePosition = addDeviceSection(1);
+ }
+
+ void addStateStats() {
+ mStateRxTimePosition = addStateSection(1);
+ mStateTxTimesCount = ModemActivityInfo.getNumTxPowerLevels();
+ mStateTxTimesPosition = addStateSection(mStateTxTimesCount);
+ }
+
+ void addUidNetworkStats() {
+ mUidRxBytesPosition = addUidSection(1);
+ mUidTxBytesPosition = addUidSection(1);
+ mUidRxPacketsPosition = addUidSection(1);
+ mUidTxPacketsPosition = addUidSection(1);
+ }
+
+ @Override
+ public void addDeviceSectionPowerEstimate() {
+ super.addDeviceSectionPowerEstimate();
+ mDeviceCallPowerPosition = addDeviceSection(1);
+ }
+
+ public void setDeviceSleepTime(long[] stats, long durationMillis) {
+ stats[mDeviceSleepTimePosition] = durationMillis;
+ }
+
+ public long getDeviceSleepTime(long[] stats) {
+ return stats[mDeviceSleepTimePosition];
+ }
+
+ public void setDeviceIdleTime(long[] stats, long durationMillis) {
+ stats[mDeviceIdleTimePosition] = durationMillis;
+ }
+
+ public long getDeviceIdleTime(long[] stats) {
+ return stats[mDeviceIdleTimePosition];
+ }
+
+ public void setDeviceScanTime(long[] stats, long durationMillis) {
+ stats[mDeviceScanTimePosition] = durationMillis;
+ }
+
+ public long getDeviceScanTime(long[] stats) {
+ return stats[mDeviceScanTimePosition];
+ }
+
+ public void setDeviceCallTime(long[] stats, long durationMillis) {
+ stats[mDeviceCallTimePosition] = durationMillis;
+ }
+
+ public long getDeviceCallTime(long[] stats) {
+ return stats[mDeviceCallTimePosition];
+ }
+
+ public void setDeviceCallPowerEstimate(long[] stats, double power) {
+ stats[mDeviceCallPowerPosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+ }
+
+ public double getDeviceCallPowerEstimate(long[] stats) {
+ return stats[mDeviceCallPowerPosition] / MILLI_TO_NANO_MULTIPLIER;
+ }
+
+ public void setStateRxTime(long[] stats, long durationMillis) {
+ stats[mStateRxTimePosition] = durationMillis;
+ }
+
+ public long getStateRxTime(long[] stats) {
+ return stats[mStateRxTimePosition];
+ }
+
+ public void setStateTxTime(long[] stats, int level, int durationMillis) {
+ stats[mStateTxTimesPosition + level] = durationMillis;
+ }
+
+ public long getStateTxTime(long[] stats, int level) {
+ return stats[mStateTxTimesPosition + level];
+ }
+
+ public void setUidRxBytes(long[] stats, long count) {
+ stats[mUidRxBytesPosition] = count;
+ }
+
+ public long getUidRxBytes(long[] stats) {
+ return stats[mUidRxBytesPosition];
+ }
+
+ public void setUidTxBytes(long[] stats, long count) {
+ stats[mUidTxBytesPosition] = count;
+ }
+
+ public long getUidTxBytes(long[] stats) {
+ return stats[mUidTxBytesPosition];
+ }
+
+ public void setUidRxPackets(long[] stats, long count) {
+ stats[mUidRxPacketsPosition] = count;
+ }
+
+ public long getUidRxPackets(long[] stats) {
+ return stats[mUidRxPacketsPosition];
+ }
+
+ public void setUidTxPackets(long[] stats, long count) {
+ stats[mUidTxPacketsPosition] = count;
+ }
+
+ public long getUidTxPackets(long[] stats) {
+ return stats[mUidTxPacketsPosition];
+ }
+
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
+ extras.putInt(EXTRA_DEVICE_SLEEP_TIME_POSITION, mDeviceSleepTimePosition);
+ extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
+ extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
+ extras.putInt(EXTRA_DEVICE_CALL_TIME_POSITION, mDeviceCallTimePosition);
+ extras.putInt(EXTRA_DEVICE_CALL_POWER_POSITION, mDeviceCallPowerPosition);
+ extras.putInt(EXTRA_STATE_RX_TIME_POSITION, mStateRxTimePosition);
+ extras.putInt(EXTRA_STATE_TX_TIMES_POSITION, mStateTxTimesPosition);
+ extras.putInt(EXTRA_STATE_TX_TIMES_COUNT, mStateTxTimesCount);
+ extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
+ extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
+ extras.putInt(EXTRA_UID_RX_PACKETS_POSITION, mUidRxPacketsPosition);
+ extras.putInt(EXTRA_UID_TX_PACKETS_POSITION, mUidTxPacketsPosition);
+ }
+
+ /**
+ * Retrieves elements of the stats array layout from <code>extras</code>
+ */
+ public void fromExtras(PersistableBundle extras) {
+ super.fromExtras(extras);
+ mDeviceSleepTimePosition = extras.getInt(EXTRA_DEVICE_SLEEP_TIME_POSITION);
+ mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
+ mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
+ mDeviceCallTimePosition = extras.getInt(EXTRA_DEVICE_CALL_TIME_POSITION);
+ mDeviceCallPowerPosition = extras.getInt(EXTRA_DEVICE_CALL_POWER_POSITION);
+ mStateRxTimePosition = extras.getInt(EXTRA_STATE_RX_TIME_POSITION);
+ mStateTxTimesPosition = extras.getInt(EXTRA_STATE_TX_TIMES_POSITION);
+ mStateTxTimesCount = extras.getInt(EXTRA_STATE_TX_TIMES_COUNT);
+ mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
+ mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
+ mUidRxPacketsPosition = extras.getInt(EXTRA_UID_RX_PACKETS_POSITION);
+ mUidTxPacketsPosition = extras.getInt(EXTRA_UID_TX_PACKETS_POSITION);
+ }
+
+ public void addRxTxTimesForRat(SparseArray<long[]> stateStats, int networkType, int freqRange,
+ long rxTime, int[] txTime) {
+ if (txTime.length != mStateTxTimesCount) {
+ Slog.wtf(TAG, "Invalid TX time array size: " + txTime.length);
+ return;
+ }
+
+ boolean nonZero = false;
+ if (rxTime != 0) {
+ nonZero = true;
+ } else {
+ for (int i = txTime.length - 1; i >= 0; i--) {
+ if (txTime[i] != 0) {
+ nonZero = true;
+ break;
+ }
+ }
+ }
+
+ if (!nonZero) {
+ return;
+ }
+
+ int rat = MobileRadioPowerStatsCollector.mapRadioAccessNetworkTypeToRadioAccessTechnology(
+ networkType);
+ int stateKey = MobileRadioPowerStatsCollector.makeStateKey(rat, freqRange);
+ long[] stats = stateStats.get(stateKey);
+ if (stats == null) {
+ stats = new long[getStateStatsArrayLength()];
+ stateStats.put(stateKey, stats);
+ }
+
+ stats[mStateRxTimePosition] += rxTime;
+ for (int i = mStateTxTimesCount - 1; i >= 0; i--) {
+ stats[mStateTxTimesPosition + i] += txTime[i];
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
new file mode 100644
index 0000000..c97c64b
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats;
+
+import android.os.BatteryStats;
+import android.telephony.CellSignalStrength;
+import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.internal.power.ModemPowerProfile;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class MobileRadioPowerStatsProcessor extends PowerStatsProcessor {
+ private static final String TAG = "MobileRadioPowerStatsProcessor";
+ private static final boolean DEBUG = false;
+
+ private static final int NUM_SIGNAL_STRENGTH_LEVELS =
+ CellSignalStrength.getNumSignalStrengthLevels();
+ private static final int IGNORE = -1;
+
+ private final UsageBasedPowerEstimator mSleepPowerEstimator;
+ private final UsageBasedPowerEstimator mIdlePowerEstimator;
+ private final UsageBasedPowerEstimator mCallPowerEstimator;
+ private final UsageBasedPowerEstimator mScanPowerEstimator;
+
+ private static class RxTxPowerEstimators {
+ UsageBasedPowerEstimator mRxPowerEstimator;
+ UsageBasedPowerEstimator[] mTxPowerEstimators =
+ new UsageBasedPowerEstimator[ModemActivityInfo.getNumTxPowerLevels()];
+ }
+
+ private final SparseArray<RxTxPowerEstimators> mRxTxPowerEstimators = new SparseArray<>();
+
+ private PowerStats.Descriptor mLastUsedDescriptor;
+ private MobileRadioPowerStatsLayout mStatsLayout;
+ // Sequence of steps for power estimation and intermediate results.
+ private PowerEstimationPlan mPlan;
+
+ private long[] mTmpDeviceStatsArray;
+ private long[] mTmpStateStatsArray;
+ private long[] mTmpUidStatsArray;
+
+ public MobileRadioPowerStatsProcessor(PowerProfile powerProfile) {
+ final double sleepDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(
+ PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP,
+ Double.NaN);
+ if (Double.isNaN(sleepDrainRateMa)) {
+ mSleepPowerEstimator = null;
+ } else {
+ mSleepPowerEstimator = new UsageBasedPowerEstimator(sleepDrainRateMa);
+ }
+
+ final double idleDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(
+ PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE,
+ Double.NaN);
+ if (Double.isNaN(idleDrainRateMa)) {
+ mIdlePowerEstimator = null;
+ } else {
+ mIdlePowerEstimator = new UsageBasedPowerEstimator(idleDrainRateMa);
+ }
+
+ // Instantiate legacy power estimators
+ double powerRadioActiveMa =
+ powerProfile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, Double.NaN);
+ if (Double.isNaN(powerRadioActiveMa)) {
+ double sum = 0;
+ sum += powerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
+ for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
+ sum += powerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+ }
+ powerRadioActiveMa = sum / (NUM_SIGNAL_STRENGTH_LEVELS + 1);
+ }
+ mCallPowerEstimator = new UsageBasedPowerEstimator(powerRadioActiveMa);
+
+ mScanPowerEstimator = new UsageBasedPowerEstimator(
+ powerProfile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0));
+
+ for (int rat = 0; rat < BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
+ final int freqCount = rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR
+ ? ServiceState.FREQUENCY_RANGE_COUNT : 1;
+ for (int freqRange = 0; freqRange < freqCount; freqRange++) {
+ mRxTxPowerEstimators.put(
+ MobileRadioPowerStatsCollector.makeStateKey(rat, freqRange),
+ buildRxTxPowerEstimators(powerProfile, rat, freqRange));
+ }
+ }
+ }
+
+ private static RxTxPowerEstimators buildRxTxPowerEstimators(PowerProfile powerProfile, int rat,
+ int freqRange) {
+ RxTxPowerEstimators estimators = new RxTxPowerEstimators();
+ long rxKey = ModemPowerProfile.getAverageBatteryDrainKey(
+ ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat, freqRange, IGNORE);
+ double rxDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(rxKey, Double.NaN);
+ if (Double.isNaN(rxDrainRateMa)) {
+ Log.w(TAG, "Unavailable Power Profile constant for key 0x"
+ + Long.toHexString(rxKey));
+ rxDrainRateMa = 0;
+ }
+ estimators.mRxPowerEstimator = new UsageBasedPowerEstimator(rxDrainRateMa);
+ for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
+ long txKey = ModemPowerProfile.getAverageBatteryDrainKey(
+ ModemPowerProfile.MODEM_DRAIN_TYPE_TX, rat, freqRange, txLevel);
+ double txDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(txKey,
+ Double.NaN);
+ if (Double.isNaN(txDrainRateMa)) {
+ Log.w(TAG, "Unavailable Power Profile constant for key 0x"
+ + Long.toHexString(txKey));
+ txDrainRateMa = 0;
+ }
+ estimators.mTxPowerEstimators[txLevel] = new UsageBasedPowerEstimator(txDrainRateMa);
+ }
+ return estimators;
+ }
+
+ private static class Intermediates {
+ /**
+ * Number of received packets
+ */
+ public long rxPackets;
+ /**
+ * Number of transmitted packets
+ */
+ public long txPackets;
+ /**
+ * Estimated power for the RX state of the modem.
+ */
+ public double rxPower;
+ /**
+ * Estimated power for the TX state of the modem.
+ */
+ public double txPower;
+ /**
+ * Estimated power for IDLE, SLEEP and CELL-SCAN states of the modem.
+ */
+ public double inactivePower;
+ /**
+ * Estimated power for IDLE, SLEEP and CELL-SCAN states of the modem.
+ */
+ public double callPower;
+ /**
+ * Measured consumed energy from power monitoring hardware (micro-coulombs)
+ */
+ public long consumedEnergy;
+ }
+
+ @Override
+ void finish(PowerComponentAggregatedPowerStats stats) {
+ if (stats.getPowerStatsDescriptor() == null) {
+ return;
+ }
+
+ unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor());
+
+ if (mPlan == null) {
+ mPlan = new PowerEstimationPlan(stats.getConfig());
+ }
+
+ for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+ DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+ Intermediates intermediates = new Intermediates();
+ estimation.intermediates = intermediates;
+ computeDevicePowerEstimates(stats, estimation.stateValues, intermediates);
+ }
+
+ if (mStatsLayout.getEnergyConsumerCount() != 0) {
+ double ratio = computeEstimateAdjustmentRatioUsingConsumedEnergy();
+ if (ratio != 1) {
+ for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+ DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+ adjustDevicePowerEstimates(stats, estimation.stateValues,
+ (Intermediates) estimation.intermediates, ratio);
+ }
+ }
+ }
+
+ combineDeviceStateEstimates();
+
+ ArrayList<Integer> uids = new ArrayList<>();
+ stats.collectUids(uids);
+ if (!uids.isEmpty()) {
+ for (int uid : uids) {
+ for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
+ computeUidRxTxTotals(stats, uid, mPlan.uidStateEstimates.get(i));
+ }
+ }
+
+ for (int uid : uids) {
+ for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
+ computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i));
+ }
+ }
+ }
+ mPlan.resetIntermediates();
+ }
+
+ private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
+ if (descriptor.equals(mLastUsedDescriptor)) {
+ return;
+ }
+
+ mLastUsedDescriptor = descriptor;
+ mStatsLayout = new MobileRadioPowerStatsLayout(descriptor);
+ mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
+ mTmpStateStatsArray = new long[descriptor.stateStatsArrayLength];
+ mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
+ }
+
+ /**
+ * Compute power estimates using the power profile.
+ */
+ private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+ int[] deviceStates, Intermediates intermediates) {
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
+ return;
+ }
+
+ for (int i = mStatsLayout.getEnergyConsumerCount() - 1; i >= 0; i--) {
+ intermediates.consumedEnergy += mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i);
+ }
+
+ if (mSleepPowerEstimator != null) {
+ intermediates.inactivePower += mSleepPowerEstimator.calculatePower(
+ mStatsLayout.getDeviceSleepTime(mTmpDeviceStatsArray));
+ }
+
+ if (mIdlePowerEstimator != null) {
+ intermediates.inactivePower += mIdlePowerEstimator.calculatePower(
+ mStatsLayout.getDeviceIdleTime(mTmpDeviceStatsArray));
+ }
+
+ if (mScanPowerEstimator != null) {
+ intermediates.inactivePower += mScanPowerEstimator.calculatePower(
+ mStatsLayout.getDeviceScanTime(mTmpDeviceStatsArray));
+ }
+
+ stats.forEachStateStatsKey(key -> {
+ RxTxPowerEstimators estimators = mRxTxPowerEstimators.get(key);
+ stats.getStateStats(mTmpStateStatsArray, key, deviceStates);
+ long rxTime = mStatsLayout.getStateRxTime(mTmpStateStatsArray);
+ intermediates.rxPower += estimators.mRxPowerEstimator.calculatePower(rxTime);
+ for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
+ long txTime = mStatsLayout.getStateTxTime(mTmpStateStatsArray, txLevel);
+ intermediates.txPower +=
+ estimators.mTxPowerEstimators[txLevel].calculatePower(txTime);
+ }
+ });
+
+ if (mCallPowerEstimator != null) {
+ intermediates.callPower = mCallPowerEstimator.calculatePower(
+ mStatsLayout.getDeviceCallTime(mTmpDeviceStatsArray));
+ }
+
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+ intermediates.rxPower + intermediates.txPower + intermediates.inactivePower);
+ mStatsLayout.setDeviceCallPowerEstimate(mTmpDeviceStatsArray, intermediates.callPower);
+ stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
+ }
+
+ /**
+ * Compute an adjustment ratio using the total power estimated using the power profile
+ * and the total power measured by hardware.
+ */
+ private double computeEstimateAdjustmentRatioUsingConsumedEnergy() {
+ long totalConsumedEnergy = 0;
+ double totalPower = 0;
+
+ for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+ Intermediates intermediates =
+ (Intermediates) mPlan.deviceStateEstimations.get(i).intermediates;
+ totalPower += intermediates.rxPower + intermediates.txPower
+ + intermediates.inactivePower + intermediates.callPower;
+ totalConsumedEnergy += intermediates.consumedEnergy;
+ }
+
+ if (totalPower == 0) {
+ return 1;
+ }
+
+ return uCtoMah(totalConsumedEnergy) / totalPower;
+ }
+
+ /**
+ * Uniformly apply the same adjustment to all power estimates in order to ensure that the total
+ * estimated power matches the measured consumed power. We are not claiming that all
+ * averages captured in the power profile have to be off by the same percentage in reality.
+ */
+ private void adjustDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+ int[] deviceStates, Intermediates intermediates, double ratio) {
+ intermediates.rxPower *= ratio;
+ intermediates.txPower *= ratio;
+ intermediates.inactivePower *= ratio;
+ intermediates.callPower *= ratio;
+
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
+ return;
+ }
+
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+ intermediates.rxPower + intermediates.txPower + intermediates.inactivePower);
+ mStatsLayout.setDeviceCallPowerEstimate(mTmpDeviceStatsArray, intermediates.callPower);
+ stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
+ }
+
+ /**
+ * This step is effectively a no-op in the cases where we track the same states for
+ * the entire device and all UIDs (e.g. screen on/off, on-battery/on-charger etc). However,
+ * if the lists of tracked states are not the same, we need to combine some estimates
+ * before distributing them proportionally to UIDs.
+ */
+ private void combineDeviceStateEstimates() {
+ for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+ CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
+ Intermediates cdseIntermediates = new Intermediates();
+ cdse.intermediates = cdseIntermediates;
+ List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
+ for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
+ DeviceStateEstimation dse = deviceStateEstimations.get(j);
+ Intermediates intermediates = (Intermediates) dse.intermediates;
+ cdseIntermediates.rxPower += intermediates.rxPower;
+ cdseIntermediates.txPower += intermediates.txPower;
+ cdseIntermediates.inactivePower += intermediates.inactivePower;
+ cdseIntermediates.consumedEnergy += intermediates.consumedEnergy;
+ }
+ }
+ }
+
+ private void computeUidRxTxTotals(PowerComponentAggregatedPowerStats stats, int uid,
+ UidStateEstimate uidStateEstimate) {
+ Intermediates intermediates =
+ (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+ for (UidStateProportionalEstimate proportionalEstimate :
+ uidStateEstimate.proportionalEstimates) {
+ if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+ continue;
+ }
+
+ intermediates.rxPackets += mStatsLayout.getUidRxPackets(mTmpUidStatsArray);
+ intermediates.txPackets += mStatsLayout.getUidTxPackets(mTmpUidStatsArray);
+ }
+ }
+
+ private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, int uid,
+ UidStateEstimate uidStateEstimate) {
+ Intermediates intermediates =
+ (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+ for (UidStateProportionalEstimate proportionalEstimate :
+ uidStateEstimate.proportionalEstimates) {
+ if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+ continue;
+ }
+
+ double power = 0;
+ if (intermediates.rxPackets != 0) {
+ power += intermediates.rxPower * mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
+ / intermediates.rxPackets;
+ }
+ if (intermediates.txPackets != 0) {
+ power += intermediates.txPower * mStatsLayout.getUidTxPackets(mTmpUidStatsArray)
+ / intermediates.txPackets;
+ }
+
+ mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+ stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+
+ if (DEBUG) {
+ Slog.d(TAG, "UID: " + uid
+ + " states: " + Arrays.toString(proportionalEstimate.stateValues)
+ + " stats: " + Arrays.toString(mTmpUidStatsArray)
+ + " rx: " + mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
+ + " rx-power: " + intermediates.rxPower
+ + " rx-packets: " + intermediates.rxPackets
+ + " tx: " + mStatsLayout.getUidTxPackets(mTmpUidStatsArray)
+ + " tx-power: " + intermediates.txPower
+ + " tx-packets: " + intermediates.txPackets
+ + " power: " + power);
+ }
+ }
+ }
+
+ @Override
+ String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ unpackPowerStatsDescriptor(descriptor);
+ return "idle: " + mStatsLayout.getDeviceIdleTime(stats)
+ + " sleep: " + mStatsLayout.getDeviceSleepTime(stats)
+ + " scan: " + mStatsLayout.getDeviceScanTime(stats)
+ + " power: " + mStatsLayout.getDevicePowerEstimate(stats);
+ }
+
+ @Override
+ String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
+ unpackPowerStatsDescriptor(descriptor);
+ StringBuilder sb = new StringBuilder();
+ sb.append(descriptor.getStateLabel(key));
+ sb.append(" rx: ").append(mStatsLayout.getStateRxTime(stats));
+ sb.append(" tx: ");
+ for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
+ if (txLevel != 0) {
+ sb.append(", ");
+ }
+ sb.append(mStatsLayout.getStateTxTime(stats, txLevel));
+ }
+ return sb.toString();
+ }
+
+ @Override
+ String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ unpackPowerStatsDescriptor(descriptor);
+ return "rx: " + mStatsLayout.getUidRxPackets(stats)
+ + " tx: " + mStatsLayout.getUidTxPackets(stats)
+ + " power: " + mStatsLayout.getUidPowerEstimate(stats);
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/MultiStateStats.java b/services/core/java/com/android/server/power/stats/MultiStateStats.java
index 9356950..6c4a2b6 100644
--- a/services/core/java/com/android/server/power/stats/MultiStateStats.java
+++ b/services/core/java/com/android/server/power/stats/MultiStateStats.java
@@ -288,6 +288,14 @@
}
/**
+ * Copies time-in-state and timestamps from the supplied prototype. Does not
+ * copy accumulated counts.
+ */
+ public void copyStatesFrom(MultiStateStats otherStats) {
+ mCounter.copyStatesFrom(otherStats.mCounter);
+ }
+
+ /**
* Updates the current composite state by changing one of the States supplied to the Factory
* constructor.
*
diff --git a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
new file mode 100644
index 0000000..62b653f
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.PersistableBundle;
+
+import com.android.internal.os.PowerStats;
+
+public class PhoneCallPowerStatsProcessor extends PowerStatsProcessor {
+ private final PowerStatsLayout mStatsLayout;
+ private final PowerStats.Descriptor mDescriptor;
+ private final long[] mTmpDeviceStats;
+ private PowerStats.Descriptor mMobileRadioStatsDescriptor;
+ private MobileRadioPowerStatsLayout mMobileRadioStatsLayout;
+ private long[] mTmpMobileRadioDeviceStats;
+
+ public PhoneCallPowerStatsProcessor() {
+ mStatsLayout = new PowerStatsLayout();
+ mStatsLayout.addDeviceSectionPowerEstimate();
+ PersistableBundle extras = new PersistableBundle();
+ mStatsLayout.toExtras(extras);
+ mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_PHONE,
+ mStatsLayout.getDeviceStatsArrayLength(), null, 0, 0, extras);
+ mTmpDeviceStats = new long[mDescriptor.statsArrayLength];
+ }
+
+ @Override
+ void finish(PowerComponentAggregatedPowerStats stats) {
+ stats.setPowerStatsDescriptor(mDescriptor);
+
+ PowerComponentAggregatedPowerStats mobileRadioStats =
+ stats.getAggregatedPowerStats().getPowerComponentStats(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+ if (mobileRadioStats == null) {
+ return;
+ }
+
+ if (mMobileRadioStatsDescriptor == null) {
+ mMobileRadioStatsDescriptor = mobileRadioStats.getPowerStatsDescriptor();
+ if (mMobileRadioStatsDescriptor == null) {
+ return;
+ }
+
+ mMobileRadioStatsLayout =
+ new MobileRadioPowerStatsLayout(
+ mMobileRadioStatsDescriptor);
+ mTmpMobileRadioDeviceStats = new long[mMobileRadioStatsDescriptor.statsArrayLength];
+ }
+
+ MultiStateStats.States[] deviceStateConfig =
+ mobileRadioStats.getConfig().getDeviceStateConfig();
+
+ // Phone call power estimates have already been calculated by the mobile radio stats
+ // processor. All that remains to be done is copy the estimates over.
+ MultiStateStats.States.forEachTrackedStateCombination(deviceStateConfig,
+ states -> {
+ mobileRadioStats.getDeviceStats(mTmpMobileRadioDeviceStats, states);
+ double callPowerEstimate =
+ mMobileRadioStatsLayout.getDeviceCallPowerEstimate(
+ mTmpMobileRadioDeviceStats);
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, callPowerEstimate);
+ stats.setDeviceStats(states, mTmpDeviceStats);
+ });
+ }
+
+ @Override
+ String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ return "power: " + mStatsLayout.getDevicePowerEstimate(stats);
+ }
+
+ @Override
+ String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
+ // Unsupported for this power component
+ return null;
+ }
+
+ @Override
+ String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ // Unsupported for this power component
+ return null;
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
index 1637022..6d58307 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.UserHandle;
import android.util.IndentingPrintWriter;
import android.util.SparseArray;
@@ -29,7 +30,10 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.function.IntConsumer;
/**
* Aggregated power stats for a specific power component (e.g. CPU, WiFi, etc). This class
@@ -41,22 +45,28 @@
static final String XML_TAG_POWER_COMPONENT = "power_component";
static final String XML_ATTR_ID = "id";
private static final String XML_TAG_DEVICE_STATS = "device-stats";
+ private static final String XML_TAG_STATE_STATS = "state-stats";
+ private static final String XML_ATTR_KEY = "key";
private static final String XML_TAG_UID_STATS = "uid-stats";
private static final String XML_ATTR_UID = "uid";
private static final long UNKNOWN = -1;
public final int powerComponentId;
- private final MultiStateStats.States[] mDeviceStateConfig;
- private final MultiStateStats.States[] mUidStateConfig;
+ @NonNull
+ private final AggregatedPowerStats mAggregatedPowerStats;
@NonNull
private final AggregatedPowerStatsConfig.PowerComponent mConfig;
+ private final MultiStateStats.States[] mDeviceStateConfig;
+ private final MultiStateStats.States[] mUidStateConfig;
private final int[] mDeviceStates;
private MultiStateStats.Factory mStatsFactory;
+ private MultiStateStats.Factory mStateStatsFactory;
private MultiStateStats.Factory mUidStatsFactory;
private PowerStats.Descriptor mPowerStatsDescriptor;
private long mPowerStatsTimestamp;
private MultiStateStats mDeviceStats;
+ private final SparseArray<MultiStateStats> mStateStats = new SparseArray<>();
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
private static class UidStats {
@@ -64,7 +74,9 @@
public MultiStateStats stats;
}
- PowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config) {
+ PowerComponentAggregatedPowerStats(@NonNull AggregatedPowerStats aggregatedPowerStats,
+ @NonNull AggregatedPowerStatsConfig.PowerComponent config) {
+ mAggregatedPowerStats = aggregatedPowerStats;
mConfig = config;
powerComponentId = config.getPowerComponentId();
mDeviceStateConfig = config.getDeviceStateConfig();
@@ -74,6 +86,11 @@
}
@NonNull
+ AggregatedPowerStats getAggregatedPowerStats() {
+ return mAggregatedPowerStats;
+ }
+
+ @NonNull
public AggregatedPowerStatsConfig.PowerComponent getConfig() {
return mConfig;
}
@@ -83,16 +100,25 @@
return mPowerStatsDescriptor;
}
- void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state, long time) {
+ public void setPowerStatsDescriptor(PowerStats.Descriptor powerStatsDescriptor) {
+ mPowerStatsDescriptor = powerStatsDescriptor;
+ }
+
+ void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state,
+ long timestampMs) {
if (mDeviceStats == null) {
- createDeviceStats();
+ createDeviceStats(timestampMs);
}
mDeviceStates[stateId] = state;
if (mDeviceStateConfig[stateId].isTracked()) {
if (mDeviceStats != null) {
- mDeviceStats.setState(stateId, state, time);
+ mDeviceStats.setState(stateId, state, timestampMs);
+ }
+ for (int i = mStateStats.size() - 1; i >= 0; i--) {
+ MultiStateStats stateStats = mStateStats.valueAt(i);
+ stateStats.setState(stateId, state, timestampMs);
}
}
@@ -100,36 +126,39 @@
for (int i = mUidStats.size() - 1; i >= 0; i--) {
PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
if (uidStats.stats == null) {
- createUidStats(uidStats);
+ createUidStats(uidStats, timestampMs);
}
uidStats.states[stateId] = state;
if (uidStats.stats != null) {
- uidStats.stats.setState(stateId, state, time);
+ uidStats.stats.setState(stateId, state, timestampMs);
}
}
}
}
void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state,
- long time) {
+ long timestampMs) {
if (!mUidStateConfig[stateId].isTracked()) {
return;
}
UidStats uidStats = getUidStats(uid);
if (uidStats.stats == null) {
- createUidStats(uidStats);
+ createUidStats(uidStats, timestampMs);
}
uidStats.states[stateId] = state;
if (uidStats.stats != null) {
- uidStats.stats.setState(stateId, state, time);
+ uidStats.stats.setState(stateId, state, timestampMs);
}
}
void setDeviceStats(@AggregatedPowerStatsConfig.TrackedState int[] states, long[] values) {
+ if (mDeviceStats == null) {
+ createDeviceStats(0);
+ }
mDeviceStats.setStats(states, values);
}
@@ -147,16 +176,24 @@
mPowerStatsDescriptor = powerStats.descriptor;
if (mDeviceStats == null) {
- createDeviceStats();
+ createDeviceStats(timestampMs);
}
+ for (int i = powerStats.stateStats.size() - 1; i >= 0; i--) {
+ int key = powerStats.stateStats.keyAt(i);
+ MultiStateStats stateStats = mStateStats.get(key);
+ if (stateStats == null) {
+ stateStats = createStateStats(key, timestampMs);
+ }
+ stateStats.increment(powerStats.stateStats.valueAt(i), timestampMs);
+ }
mDeviceStats.increment(powerStats.stats, timestampMs);
for (int i = powerStats.uidStats.size() - 1; i >= 0; i--) {
int uid = powerStats.uidStats.keyAt(i);
PowerComponentAggregatedPowerStats.UidStats uidStats = getUidStats(uid);
if (uidStats.stats == null) {
- createUidStats(uidStats);
+ createUidStats(uidStats, timestampMs);
}
uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs);
}
@@ -168,6 +205,7 @@
mStatsFactory = null;
mUidStatsFactory = null;
mDeviceStats = null;
+ mStateStats.clear();
for (int i = mUidStats.size() - 1; i >= 0; i--) {
mUidStats.valueAt(i).stats = null;
}
@@ -178,6 +216,13 @@
if (uidStats == null) {
uidStats = new UidStats();
uidStats.states = new int[mUidStateConfig.length];
+ for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
+ if (mUidStateConfig[stateId].isTracked()
+ && stateId < mDeviceStateConfig.length
+ && mDeviceStateConfig[stateId].isTracked()) {
+ uidStats.states[stateId] = mDeviceStates[stateId];
+ }
+ }
mUidStats.put(uid, uidStats);
}
return uidStats;
@@ -204,6 +249,26 @@
return false;
}
+ boolean getStateStats(long[] outValues, int key, int[] deviceStates) {
+ if (deviceStates.length != mDeviceStateConfig.length) {
+ throw new IllegalArgumentException(
+ "Invalid number of tracked states: " + deviceStates.length
+ + " expected: " + mDeviceStateConfig.length);
+ }
+ MultiStateStats stateStats = mStateStats.get(key);
+ if (stateStats != null) {
+ stateStats.getStats(outValues, deviceStates);
+ return true;
+ }
+ return false;
+ }
+
+ void forEachStateStatsKey(IntConsumer consumer) {
+ for (int i = mStateStats.size() - 1; i >= 0; i--) {
+ consumer.accept(mStateStats.keyAt(i));
+ }
+ }
+
boolean getUidStats(long[] outValues, int uid, int[] uidStates) {
if (uidStates.length != mUidStateConfig.length) {
throw new IllegalArgumentException(
@@ -218,7 +283,7 @@
return false;
}
- private void createDeviceStats() {
+ private void createDeviceStats(long timestampMs) {
if (mStatsFactory == null) {
if (mPowerStatsDescriptor == null) {
return;
@@ -229,13 +294,39 @@
mDeviceStats = mStatsFactory.create();
if (mPowerStatsTimestamp != UNKNOWN) {
+ timestampMs = mPowerStatsTimestamp;
+ }
+ if (timestampMs != UNKNOWN) {
for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
- mDeviceStats.setState(stateId, mDeviceStates[stateId], mPowerStatsTimestamp);
+ int state = mDeviceStates[stateId];
+ mDeviceStats.setState(stateId, state, timestampMs);
+ for (int i = mStateStats.size() - 1; i >= 0; i--) {
+ MultiStateStats stateStats = mStateStats.valueAt(i);
+ stateStats.setState(stateId, state, timestampMs);
+ }
}
}
}
- private void createUidStats(UidStats uidStats) {
+ private MultiStateStats createStateStats(int key, long timestampMs) {
+ if (mStateStatsFactory == null) {
+ if (mPowerStatsDescriptor == null) {
+ return null;
+ }
+ mStateStatsFactory = new MultiStateStats.Factory(
+ mPowerStatsDescriptor.stateStatsArrayLength, mDeviceStateConfig);
+ }
+
+ MultiStateStats stateStats = mStateStatsFactory.create();
+ mStateStats.put(key, stateStats);
+ if (mDeviceStats != null) {
+ stateStats.copyStatesFrom(mDeviceStats);
+ }
+
+ return stateStats;
+ }
+
+ private void createUidStats(UidStats uidStats, long timestampMs) {
if (mUidStatsFactory == null) {
if (mPowerStatsDescriptor == null) {
return;
@@ -245,9 +336,13 @@
}
uidStats.stats = mUidStatsFactory.create();
- for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
- if (mPowerStatsTimestamp != UNKNOWN) {
- uidStats.stats.setState(stateId, uidStats.states[stateId], mPowerStatsTimestamp);
+
+ if (mPowerStatsTimestamp != UNKNOWN) {
+ timestampMs = mPowerStatsTimestamp;
+ }
+ if (timestampMs != UNKNOWN) {
+ for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
+ uidStats.stats.setState(stateId, uidStats.states[stateId], timestampMs);
}
}
}
@@ -268,6 +363,13 @@
serializer.endTag(null, XML_TAG_DEVICE_STATS);
}
+ for (int i = 0; i < mStateStats.size(); i++) {
+ serializer.startTag(null, XML_TAG_STATE_STATS);
+ serializer.attributeInt(null, XML_ATTR_KEY, mStateStats.keyAt(i));
+ mStateStats.valueAt(i).writeXml(serializer);
+ serializer.endTag(null, XML_TAG_STATE_STATS);
+ }
+
for (int i = mUidStats.size() - 1; i >= 0; i--) {
int uid = mUidStats.keyAt(i);
UidStats uidStats = mUidStats.valueAt(i);
@@ -285,8 +387,10 @@
public boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException,
IOException {
+ String outerTag = parser.getName();
int eventType = parser.getEventType();
- while (eventType != XmlPullParser.END_DOCUMENT) {
+ while (eventType != XmlPullParser.END_DOCUMENT
+ && !(eventType == XmlPullParser.END_TAG && parser.getName().equals(outerTag))) {
if (eventType == XmlPullParser.START_TAG) {
switch (parser.getName()) {
case PowerStats.Descriptor.XML_TAG_DESCRIPTOR:
@@ -297,17 +401,27 @@
break;
case XML_TAG_DEVICE_STATS:
if (mDeviceStats == null) {
- createDeviceStats();
+ createDeviceStats(UNKNOWN);
}
if (!mDeviceStats.readFromXml(parser)) {
return false;
}
break;
+ case XML_TAG_STATE_STATS:
+ int key = parser.getAttributeInt(null, XML_ATTR_KEY);
+ MultiStateStats stats = mStateStats.get(key);
+ if (stats == null) {
+ stats = createStateStats(key, UNKNOWN);
+ }
+ if (!stats.readFromXml(parser)) {
+ return false;
+ }
+ break;
case XML_TAG_UID_STATS:
int uid = parser.getAttributeInt(null, XML_ATTR_UID);
UidStats uidStats = getUidStats(uid);
if (uidStats.stats == null) {
- createUidStats(uidStats);
+ createUidStats(uidStats, UNKNOWN);
}
if (!uidStats.stats.readFromXml(parser)) {
return false;
@@ -328,6 +442,21 @@
mConfig.getProcessor().deviceStatsToString(mPowerStatsDescriptor, stats));
ipw.decreaseIndent();
}
+
+ if (mStateStats.size() != 0) {
+ ipw.increaseIndent();
+ ipw.println(mPowerStatsDescriptor.name + " states");
+ ipw.increaseIndent();
+ for (int i = 0; i < mStateStats.size(); i++) {
+ int key = mStateStats.keyAt(i);
+ MultiStateStats stateStats = mStateStats.valueAt(i);
+ stateStats.dump(ipw, stats ->
+ mConfig.getProcessor().stateStatsToString(mPowerStatsDescriptor, key,
+ stats));
+ }
+ ipw.decreaseIndent();
+ ipw.decreaseIndent();
+ }
}
void dumpUid(IndentingPrintWriter ipw, int uid) {
@@ -340,4 +469,29 @@
ipw.decreaseIndent();
}
}
+
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ IndentingPrintWriter ipw = new IndentingPrintWriter(sw);
+ ipw.increaseIndent();
+ dumpDevice(ipw);
+ ipw.decreaseIndent();
+
+ int[] uids = new int[mUidStats.size()];
+ for (int i = uids.length - 1; i >= 0; i--) {
+ uids[i] = mUidStats.keyAt(i);
+ }
+ Arrays.sort(uids);
+ for (int uid : uids) {
+ ipw.println(UserHandle.formatUid(uid));
+ ipw.increaseIndent();
+ dumpUid(ipw, uid);
+ ipw.decreaseIndent();
+ }
+
+ ipw.flush();
+
+ return sw.toString();
+ }
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
index ba4c127..6a4c1f0 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
@@ -32,7 +32,7 @@
private static final long UNINITIALIZED = -1;
private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
private final BatteryStatsHistory mHistory;
- private final SparseArray<AggregatedPowerStatsProcessor> mProcessors = new SparseArray<>();
+ private final SparseArray<PowerStatsProcessor> mProcessors = new SparseArray<>();
private AggregatedPowerStats mStats;
private int mCurrentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
private int mCurrentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
@@ -43,7 +43,7 @@
mHistory = history;
for (AggregatedPowerStatsConfig.PowerComponent powerComponentsConfig :
aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs()) {
- AggregatedPowerStatsProcessor processor = powerComponentsConfig.getProcessor();
+ PowerStatsProcessor processor = powerComponentsConfig.getProcessor();
mProcessors.put(powerComponentsConfig.getPowerComponentId(), processor);
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index c76797ba..b82c021 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -17,9 +17,12 @@
package com.android.server.power.stats;
import android.annotation.Nullable;
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
import android.os.ConditionVariable;
import android.os.Handler;
-import android.os.PersistableBundle;
+import android.power.PowerStatsInternal;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -30,7 +33,12 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
/**
@@ -43,214 +51,38 @@
public abstract class PowerStatsCollector {
private static final String TAG = "PowerStatsCollector";
private static final int MILLIVOLTS_PER_VOLT = 1000;
+ private static final long POWER_STATS_ENERGY_CONSUMERS_TIMEOUT = 20000;
private final Handler mHandler;
+ protected final PowerStatsUidResolver mUidResolver;
protected final Clock mClock;
private final long mThrottlePeriodMs;
private final Runnable mCollectAndDeliverStats = this::collectAndDeliverStats;
private boolean mEnabled;
private long mLastScheduledUpdateMs = -1;
- /**
- * Captures the positions and lengths of sections of the stats array, such as usage duration,
- * power usage estimates etc.
- */
- public static class StatsArrayLayout {
- private static final String EXTRA_DEVICE_POWER_POSITION = "dp";
- private static final String EXTRA_DEVICE_DURATION_POSITION = "dd";
- private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
- private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
- private static final String EXTRA_UID_POWER_POSITION = "up";
-
- protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
-
- private int mDeviceStatsArrayLength;
- private int mUidStatsArrayLength;
-
- protected int mDeviceDurationPosition;
- private int mDeviceEnergyConsumerPosition;
- private int mDeviceEnergyConsumerCount;
- private int mDevicePowerEstimatePosition;
- private int mUidPowerEstimatePosition;
-
- public int getDeviceStatsArrayLength() {
- return mDeviceStatsArrayLength;
- }
-
- public int getUidStatsArrayLength() {
- return mUidStatsArrayLength;
- }
-
- protected int addDeviceSection(int length) {
- int position = mDeviceStatsArrayLength;
- mDeviceStatsArrayLength += length;
- return position;
- }
-
- protected int addUidSection(int length) {
- int position = mUidStatsArrayLength;
- mUidStatsArrayLength += length;
- return position;
- }
-
- /**
- * Declare that the stats array has a section capturing usage duration
- */
- public void addDeviceSectionUsageDuration() {
- mDeviceDurationPosition = addDeviceSection(1);
- }
-
- /**
- * Saves the usage duration in the corresponding <code>stats</code> element.
- */
- public void setUsageDuration(long[] stats, long value) {
- stats[mDeviceDurationPosition] = value;
- }
-
- /**
- * Extracts the usage duration from the corresponding <code>stats</code> element.
- */
- public long getUsageDuration(long[] stats) {
- return stats[mDeviceDurationPosition];
- }
-
- /**
- * Declares that the stats array has a section capturing EnergyConsumer data from
- * PowerStatsService.
- */
- public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
- mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount);
- mDeviceEnergyConsumerCount = energyConsumerCount;
- }
-
- public int getEnergyConsumerCount() {
- return mDeviceEnergyConsumerCount;
- }
-
- /**
- * Saves the accumulated energy for the specified rail the corresponding
- * <code>stats</code> element.
- */
- public void setConsumedEnergy(long[] stats, int index, long energy) {
- stats[mDeviceEnergyConsumerPosition + index] = energy;
- }
-
- /**
- * Extracts the EnergyConsumer data from a device stats array for the specified
- * EnergyConsumer.
- */
- public long getConsumedEnergy(long[] stats, int index) {
- return stats[mDeviceEnergyConsumerPosition + index];
- }
-
- /**
- * Declare that the stats array has a section capturing a power estimate
- */
- public void addDeviceSectionPowerEstimate() {
- mDevicePowerEstimatePosition = addDeviceSection(1);
- }
-
- /**
- * Converts the supplied mAh power estimate to a long and saves it in the corresponding
- * element of <code>stats</code>.
- */
- public void setDevicePowerEstimate(long[] stats, double power) {
- stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
- }
-
- /**
- * Extracts the power estimate from a device stats array and converts it to mAh.
- */
- public double getDevicePowerEstimate(long[] stats) {
- return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
- }
-
- /**
- * Declare that the UID stats array has a section capturing a power estimate
- */
- public void addUidSectionPowerEstimate() {
- mUidPowerEstimatePosition = addUidSection(1);
- }
-
- /**
- * Converts the supplied mAh power estimate to a long and saves it in the corresponding
- * element of <code>stats</code>.
- */
- public void setUidPowerEstimate(long[] stats, double power) {
- stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
- }
-
- /**
- * Extracts the power estimate from a UID stats array and converts it to mAh.
- */
- public double getUidPowerEstimate(long[] stats) {
- return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
- }
-
- /**
- * Copies the elements of the stats array layout into <code>extras</code>
- */
- public void toExtras(PersistableBundle extras) {
- extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition);
- extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION,
- mDeviceEnergyConsumerPosition);
- extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT,
- mDeviceEnergyConsumerCount);
- extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
- extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
- }
-
- /**
- * Retrieves elements of the stats array layout from <code>extras</code>
- */
- public void fromExtras(PersistableBundle extras) {
- mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION);
- mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
- mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
- mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
- mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
- }
-
- protected void putIntArray(PersistableBundle extras, String key, int[] array) {
- if (array == null) {
- return;
- }
-
- StringBuilder sb = new StringBuilder();
- for (int value : array) {
- if (!sb.isEmpty()) {
- sb.append(',');
- }
- sb.append(value);
- }
- extras.putString(key, sb.toString());
- }
-
- protected int[] getIntArray(PersistableBundle extras, String key) {
- String string = extras.getString(key);
- if (string == null) {
- return null;
- }
- String[] values = string.trim().split(",");
- int[] result = new int[values.length];
- for (int i = 0; i < values.length; i++) {
- try {
- result[i] = Integer.parseInt(values[i]);
- } catch (NumberFormatException e) {
- Slog.wtf(TAG, "Invalid CSV format: " + string);
- return null;
- }
- }
- return result;
- }
- }
-
@GuardedBy("this")
@SuppressWarnings("unchecked")
private volatile List<Consumer<PowerStats>> mConsumerList = Collections.emptyList();
- public PowerStatsCollector(Handler handler, long throttlePeriodMs, Clock clock) {
+ public PowerStatsCollector(Handler handler, long throttlePeriodMs,
+ PowerStatsUidResolver uidResolver, Clock clock) {
mHandler = handler;
mThrottlePeriodMs = throttlePeriodMs;
+ mUidResolver = uidResolver;
+ mUidResolver.addListener(new PowerStatsUidResolver.Listener() {
+ @Override
+ public void onIsolatedUidAdded(int isolatedUid, int parentUid) {
+ }
+
+ @Override
+ public void onBeforeIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ }
+
+ @Override
+ public void onAfterIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ mHandler.post(()->onUidRemoved(isolatedUid));
+ }
+ });
mClock = clock;
}
@@ -388,10 +220,87 @@
done.block();
}
+ protected void onUidRemoved(int uid) {
+ }
+
/** Calculate charge consumption (in microcoulombs) from a given energy and voltage */
- protected long uJtoUc(long deltaEnergyUj, int avgVoltageMv) {
+ protected static long uJtoUc(long deltaEnergyUj, int avgVoltageMv) {
// To overflow, a 3.7V 10000mAh battery would need to completely drain 69244 times
// since the last snapshot. Round off to the nearest whole long.
return (deltaEnergyUj * MILLIVOLTS_PER_VOLT + (avgVoltageMv / 2)) / avgVoltageMv;
}
+
+ interface ConsumedEnergyRetriever {
+ int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType);
+
+ @Nullable
+ long[] getConsumedEnergyUws(int[] energyConsumerIds);
+ }
+
+ static class ConsumedEnergyRetrieverImpl implements ConsumedEnergyRetriever {
+ private final PowerStatsInternal mPowerStatsInternal;
+
+ ConsumedEnergyRetrieverImpl(PowerStatsInternal powerStatsInternal) {
+ mPowerStatsInternal = powerStatsInternal;
+ }
+
+ @Override
+ public int[] getEnergyConsumerIds(int energyConsumerType) {
+ if (mPowerStatsInternal == null) {
+ return new int[0];
+ }
+
+ EnergyConsumer[] energyConsumerInfo = mPowerStatsInternal.getEnergyConsumerInfo();
+ if (energyConsumerInfo == null) {
+ return new int[0];
+ }
+
+ List<EnergyConsumer> energyConsumers = new ArrayList<>();
+ for (EnergyConsumer energyConsumer : energyConsumerInfo) {
+ if (energyConsumer.type == energyConsumerType) {
+ energyConsumers.add(energyConsumer);
+ }
+ }
+ if (energyConsumers.isEmpty()) {
+ return new int[0];
+ }
+
+ energyConsumers.sort(Comparator.comparing(c -> c.ordinal));
+
+ int[] ids = new int[energyConsumers.size()];
+ for (int i = 0; i < ids.length; i++) {
+ ids[i] = energyConsumers.get(i).id;
+ }
+ return ids;
+ }
+
+ @Override
+ public long[] getConsumedEnergyUws(int[] energyConsumerIds) {
+ CompletableFuture<EnergyConsumerResult[]> future =
+ mPowerStatsInternal.getEnergyConsumedAsync(energyConsumerIds);
+ EnergyConsumerResult[] results = null;
+ try {
+ results = future.get(
+ POWER_STATS_ENERGY_CONSUMERS_TIMEOUT, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Slog.e(TAG, "Could not obtain energy consumers from PowerStatsService", e);
+ }
+
+ if (results == null) {
+ return null;
+ }
+
+ long[] energy = new long[energyConsumerIds.length];
+ for (int i = 0; i < energyConsumerIds.length; i++) {
+ int id = energyConsumerIds[i];
+ for (EnergyConsumerResult result : results) {
+ if (result.id == id) {
+ energy[i] = result.energyUWs;
+ break;
+ }
+ }
+ }
+ return energy;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
index 4f4ddca..f6b198a8 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
@@ -139,7 +139,7 @@
return;
}
- PowerStatsCollector.StatsArrayLayout layout = new PowerStatsCollector.StatsArrayLayout();
+ PowerStatsLayout layout = new PowerStatsLayout();
layout.fromExtras(descriptor.extras);
long[] deviceStats = new long[descriptor.statsArrayLength];
@@ -164,9 +164,20 @@
deviceScope.addConsumedPower(powerComponentId,
totalPower[0], BatteryConsumer.POWER_MODEL_UNDEFINED);
+ if (layout.isUidPowerAttributionSupported()) {
+ populateUidBatteryConsumers(batteryUsageStatsBuilder, powerComponent,
+ powerComponentStats, layout);
+ }
+ }
+
+ private static void populateUidBatteryConsumers(
+ BatteryUsageStats.Builder batteryUsageStatsBuilder,
+ AggregatedPowerStatsConfig.PowerComponent powerComponent,
+ PowerComponentAggregatedPowerStats powerComponentStats,
+ PowerStatsLayout layout) {
+ int powerComponentId = powerComponent.getPowerComponentId();
+ PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor();
long[] uidStats = new long[descriptor.uidStatsArrayLength];
- ArrayList<Integer> uids = new ArrayList<>();
- powerComponentStats.collectUids(uids);
boolean breakDownByProcState =
batteryUsageStatsBuilder.isProcessStateDataNeeded()
@@ -177,6 +188,8 @@
double[] powerByProcState =
new double[breakDownByProcState ? BatteryConsumer.PROCESS_STATE_COUNT : 1];
double powerAllApps = 0;
+ ArrayList<Integer> uids = new ArrayList<>();
+ powerComponentStats.collectUids(uids);
for (int uid : uids) {
UidBatteryConsumer.Builder builder =
batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid);
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
new file mode 100644
index 0000000..aa96409
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.os.PersistableBundle;
+import android.util.Slog;
+
+import com.android.internal.os.PowerStats;
+
+/**
+ * Captures the positions and lengths of sections of the stats array, such as usage duration,
+ * power usage estimates etc.
+ */
+public class PowerStatsLayout {
+ private static final String TAG = "PowerStatsLayout";
+ private static final String EXTRA_DEVICE_POWER_POSITION = "dp";
+ private static final String EXTRA_DEVICE_DURATION_POSITION = "dd";
+ private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
+ private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
+ private static final String EXTRA_UID_POWER_POSITION = "up";
+
+ protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
+ protected static final int UNSUPPORTED = -1;
+
+ private int mDeviceStatsArrayLength;
+ private int mStateStatsArrayLength;
+ private int mUidStatsArrayLength;
+
+ protected int mDeviceDurationPosition = UNSUPPORTED;
+ private int mDeviceEnergyConsumerPosition;
+ private int mDeviceEnergyConsumerCount;
+ private int mDevicePowerEstimatePosition = UNSUPPORTED;
+ private int mUidPowerEstimatePosition = UNSUPPORTED;
+
+ public PowerStatsLayout() {
+ }
+
+ public PowerStatsLayout(PowerStats.Descriptor descriptor) {
+ fromExtras(descriptor.extras);
+ }
+
+ public int getDeviceStatsArrayLength() {
+ return mDeviceStatsArrayLength;
+ }
+
+ public int getStateStatsArrayLength() {
+ return mStateStatsArrayLength;
+ }
+
+ public int getUidStatsArrayLength() {
+ return mUidStatsArrayLength;
+ }
+
+ protected int addDeviceSection(int length) {
+ int position = mDeviceStatsArrayLength;
+ mDeviceStatsArrayLength += length;
+ return position;
+ }
+
+ protected int addStateSection(int length) {
+ int position = mStateStatsArrayLength;
+ mStateStatsArrayLength += length;
+ return position;
+ }
+
+ protected int addUidSection(int length) {
+ int position = mUidStatsArrayLength;
+ mUidStatsArrayLength += length;
+ return position;
+ }
+
+ /**
+ * Declare that the stats array has a section capturing usage duration
+ */
+ public void addDeviceSectionUsageDuration() {
+ mDeviceDurationPosition = addDeviceSection(1);
+ }
+
+ /**
+ * Saves the usage duration in the corresponding <code>stats</code> element.
+ */
+ public void setUsageDuration(long[] stats, long value) {
+ stats[mDeviceDurationPosition] = value;
+ }
+
+ /**
+ * Extracts the usage duration from the corresponding <code>stats</code> element.
+ */
+ public long getUsageDuration(long[] stats) {
+ return stats[mDeviceDurationPosition];
+ }
+
+ /**
+ * Declares that the stats array has a section capturing EnergyConsumer data from
+ * PowerStatsService.
+ */
+ public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
+ mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount);
+ mDeviceEnergyConsumerCount = energyConsumerCount;
+ }
+
+ public int getEnergyConsumerCount() {
+ return mDeviceEnergyConsumerCount;
+ }
+
+ /**
+ * Saves the accumulated energy for the specified rail the corresponding
+ * <code>stats</code> element.
+ */
+ public void setConsumedEnergy(long[] stats, int index, long energy) {
+ stats[mDeviceEnergyConsumerPosition + index] = energy;
+ }
+
+ /**
+ * Extracts the EnergyConsumer data from a device stats array for the specified
+ * EnergyConsumer.
+ */
+ public long getConsumedEnergy(long[] stats, int index) {
+ return stats[mDeviceEnergyConsumerPosition + index];
+ }
+
+ /**
+ * Declare that the stats array has a section capturing a power estimate
+ */
+ public void addDeviceSectionPowerEstimate() {
+ mDevicePowerEstimatePosition = addDeviceSection(1);
+ }
+
+ /**
+ * Converts the supplied mAh power estimate to a long and saves it in the corresponding
+ * element of <code>stats</code>.
+ */
+ public void setDevicePowerEstimate(long[] stats, double power) {
+ stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+ }
+
+ /**
+ * Extracts the power estimate from a device stats array and converts it to mAh.
+ */
+ public double getDevicePowerEstimate(long[] stats) {
+ return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
+ }
+
+ /**
+ * Declare that the UID stats array has a section capturing a power estimate
+ */
+ public void addUidSectionPowerEstimate() {
+ mUidPowerEstimatePosition = addUidSection(1);
+ }
+
+ /**
+ * Returns true if power for this component is attributed to UIDs (apps).
+ */
+ public boolean isUidPowerAttributionSupported() {
+ return mUidPowerEstimatePosition != UNSUPPORTED;
+ }
+
+ /**
+ * Converts the supplied mAh power estimate to a long and saves it in the corresponding
+ * element of <code>stats</code>.
+ */
+ public void setUidPowerEstimate(long[] stats, double power) {
+ stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+ }
+
+ /**
+ * Extracts the power estimate from a UID stats array and converts it to mAh.
+ */
+ public double getUidPowerEstimate(long[] stats) {
+ return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
+ }
+
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition);
+ extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION,
+ mDeviceEnergyConsumerPosition);
+ extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT,
+ mDeviceEnergyConsumerCount);
+ extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
+ extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
+ }
+
+ /**
+ * Retrieves elements of the stats array layout from <code>extras</code>
+ */
+ public void fromExtras(PersistableBundle extras) {
+ mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION);
+ mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
+ mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
+ mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
+ mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
+ }
+
+ protected void putIntArray(PersistableBundle extras, String key, int[] array) {
+ if (array == null) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int value : array) {
+ if (!sb.isEmpty()) {
+ sb.append(',');
+ }
+ sb.append(value);
+ }
+ extras.putString(key, sb.toString());
+ }
+
+ protected int[] getIntArray(PersistableBundle extras, String key) {
+ String string = extras.getString(key);
+ if (string == null) {
+ return null;
+ }
+ String[] values = string.trim().split(",");
+ int[] result = new int[values.length];
+ for (int i = 0; i < values.length; i++) {
+ try {
+ result[i] = Integer.parseInt(values[i]);
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Invalid CSV format: " + string);
+ return null;
+ }
+ }
+ return result;
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
similarity index 98%
rename from services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
index 7feb964..0d5c542 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
@@ -27,7 +27,7 @@
import java.util.List;
/*
- * The power estimation algorithm used by AggregatedPowerStatsProcessor can roughly be
+ * The power estimation algorithm used by PowerStatsProcessor can roughly be
* described like this:
*
* 1. Estimate power usage for each state combination (e.g. power-battery/screen-on) using
@@ -39,8 +39,8 @@
* 2. For each UID, compute the proportion of the combined estimates in each state
* and attribute the corresponding portion of the total power estimate in that state to the UID.
*/
-abstract class AggregatedPowerStatsProcessor {
- private static final String TAG = "AggregatedPowerStatsProcessor";
+abstract class PowerStatsProcessor {
+ private static final String TAG = "PowerStatsProcessor";
private static final int INDEX_DOES_NOT_EXIST = -1;
private static final double MILLIAMPHOUR_PER_MICROCOULOMB = 1.0 / 1000.0 / 60.0 / 60.0;
@@ -49,6 +49,8 @@
abstract String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats);
+ abstract String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats);
+
abstract String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats);
protected static class PowerEstimationPlan {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 2a93255..c8bcc51 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -54,8 +54,10 @@
import android.os.UserManager;
import android.os.ext.SdkExtensions;
import android.provider.DeviceConfig;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.LongArrayQueue;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -173,6 +175,8 @@
// Accessed on the handler thread only.
private long mRelativeBootTime = calculateRelativeBootTime();
+ private final ArrayMap<Integer, Pair<Context, BroadcastReceiver>> mUserBroadcastReceivers;
+
RollbackManagerServiceImpl(Context context) {
mContext = context;
// Note that we're calling onStart here because this object is only constructed on
@@ -210,6 +214,8 @@
}
});
+ mUserBroadcastReceivers = new ArrayMap<>();
+
UserManager userManager = mContext.getSystemService(UserManager.class);
for (UserHandle user : userManager.getUserHandles(true)) {
registerUserCallbacks(user);
@@ -275,7 +281,9 @@
}
}, enableRollbackTimedOutFilter, null, getHandler());
- IntentFilter userAddedIntentFilter = new IntentFilter(Intent.ACTION_USER_ADDED);
+ IntentFilter userIntentFilter = new IntentFilter();
+ userIntentFilter.addAction(Intent.ACTION_USER_ADDED);
+ userIntentFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -287,9 +295,15 @@
return;
}
registerUserCallbacks(UserHandle.of(newUserId));
+ } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
+ final int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (newUserId == -1) {
+ return;
+ }
+ unregisterUserCallbacks(UserHandle.of(newUserId));
}
}
- }, userAddedIntentFilter, null, getHandler());
+ }, userIntentFilter, null, getHandler());
registerTimeChangeReceiver();
}
@@ -335,7 +349,7 @@
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
filter.addDataScheme("package");
- context.registerReceiver(new BroadcastReceiver() {
+ BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
assertInWorkerThread();
@@ -354,7 +368,21 @@
onPackageFullyRemoved(packageName);
}
}
- }, filter, null, getHandler());
+ };
+ context.registerReceiver(receiver, filter, null, getHandler());
+ mUserBroadcastReceivers.put(user.getIdentifier(), new Pair(context, receiver));
+ }
+
+ @AnyThread
+ private void unregisterUserCallbacks(UserHandle user) {
+ Pair<Context, BroadcastReceiver> pair = mUserBroadcastReceivers.get(user.getIdentifier());
+ if (pair == null || pair.first == null || pair.second == null) {
+ Slog.e(TAG, "No receiver found for the user" + user);
+ return;
+ }
+
+ pair.first.unregisterReceiver(pair.second);
+ mUserBroadcastReceivers.remove(user.getIdentifier());
}
@ExtThread
diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
similarity index 100%
rename from packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
rename to services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
similarity index 100%
rename from packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
rename to services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index bb4876b..5b501e1 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -170,6 +170,10 @@
@Override
public int setupFsverity(android.os.IInstalld.IFsveritySetupAuthToken authToken,
String filePath, String packageName) throws RemoteException {
+ getContext().enforceCallingPermission(android.Manifest.permission.SETUP_FSVERITY,
+ "Permission android.permission.SETUP_FSVERITY not grantted to access "
+ + "FileIntegrityManager#setupFsverity");
+
Objects.requireNonNull(authToken);
Objects.requireNonNull(filePath);
Objects.requireNonNull(packageName);
diff --git a/services/core/java/com/android/server/selinux/QuotaLimiter.java b/services/core/java/com/android/server/selinux/QuotaLimiter.java
index e89ddfd..34d18ce 100644
--- a/services/core/java/com/android/server/selinux/QuotaLimiter.java
+++ b/services/core/java/com/android/server/selinux/QuotaLimiter.java
@@ -34,10 +34,10 @@
private final Clock mClock;
private final Duration mWindowSize;
- private final int mMaxPermits;
- private long mCurrentWindow = 0;
- private int mPermitsGranted = 0;
+ private int mMaxPermits;
+ private long mCurrentWindow;
+ private int mPermitsGranted;
@VisibleForTesting
QuotaLimiter(Clock clock, Duration windowSize, int maxPermits) {
@@ -75,4 +75,8 @@
return false;
}
+
+ public void setMaxPermits(int maxPermits) {
+ this.mMaxPermits = maxPermits;
+ }
}
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogBuilder.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogBuilder.java
index 8d8d596..d69150d 100644
--- a/services/core/java/com/android/server/selinux/SelinuxAuditLogBuilder.java
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogBuilder.java
@@ -15,35 +15,66 @@
*/
package com.android.server.selinux;
+import android.provider.DeviceConfig;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.Arrays;
import java.util.Iterator;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
import java.util.stream.Stream;
/** Builder for SelinuxAuditLogs. */
class SelinuxAuditLogBuilder {
- // Currently logs collection is hardcoded for the sdk_sandbox_audit.
- private static final String SDK_SANDBOX_AUDIT = "sdk_sandbox_audit";
- static final Matcher SCONTEXT_MATCHER =
- Pattern.compile(
- "u:r:(?<stype>"
- + SDK_SANDBOX_AUDIT
- + "):s0(:c)?(?<scategories>((,c)?\\d+)+)*")
- .matcher("");
+ private static final String TAG = "SelinuxAuditLogs";
- static final Matcher TCONTEXT_MATCHER =
- Pattern.compile("u:object_r:(?<ttype>\\w+):s0(:c)?(?<tcategories>((,c)?\\d+)+)*")
- .matcher("");
+ // This config indicates which Selinux logs for source domains to collect. The string will be
+ // inserted into a regex, so it must follow the regex syntax. For example, a valid value would
+ // be "system_server|untrusted_app".
+ @VisibleForTesting static final String CONFIG_SELINUX_AUDIT_DOMAIN = "selinux_audit_domain";
+ private static final Matcher NO_OP_MATCHER = Pattern.compile("no-op^").matcher("");
+ private static final String TCONTEXT_PATTERN =
+ "u:object_r:(?<ttype>\\w+):s0(:c)?(?<tcategories>((,c)?\\d+)+)*";
+ private static final String PATH_PATTERN = "\"(?<path>/\\w+(/\\w+)?)(/\\w+)*\"";
- static final Matcher PATH_MATCHER =
- Pattern.compile("\"(?<path>/\\w+(/\\w+)?)(/\\w+)*\"").matcher("");
+ @VisibleForTesting final Matcher mScontextMatcher;
+ @VisibleForTesting final Matcher mTcontextMatcher;
+ @VisibleForTesting final Matcher mPathMatcher;
private Iterator<String> mTokens;
private final SelinuxAuditLog mAuditLog = new SelinuxAuditLog();
+ SelinuxAuditLogBuilder() {
+ Matcher scontextMatcher = NO_OP_MATCHER;
+ Matcher tcontextMatcher = NO_OP_MATCHER;
+ Matcher pathMatcher = NO_OP_MATCHER;
+ try {
+ scontextMatcher =
+ Pattern.compile(
+ TextUtils.formatSimple(
+ "u:r:(?<stype>%s):s0(:c)?(?<scategories>((,c)?\\d+)+)*",
+ DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ CONFIG_SELINUX_AUDIT_DOMAIN,
+ "no_match^")))
+ .matcher("");
+ tcontextMatcher = Pattern.compile(TCONTEXT_PATTERN).matcher("");
+ pathMatcher = Pattern.compile(PATH_PATTERN).matcher("");
+ } catch (PatternSyntaxException e) {
+ Slog.e(TAG, "Invalid pattern, setting every matcher to no-op.", e);
+ }
+
+ mScontextMatcher = scontextMatcher;
+ mTcontextMatcher = tcontextMatcher;
+ mPathMatcher = pathMatcher;
+ }
+
void reset(String denialString) {
mTokens =
Arrays.asList(
@@ -82,18 +113,18 @@
mAuditLog.mPermissions = permissionsStream.build().toArray(String[]::new);
break;
case "scontext":
- if (!nextTokenMatches(SCONTEXT_MATCHER)) {
+ if (!nextTokenMatches(mScontextMatcher)) {
return null;
}
- mAuditLog.mSType = SCONTEXT_MATCHER.group("stype");
- mAuditLog.mSCategories = toCategories(SCONTEXT_MATCHER.group("scategories"));
+ mAuditLog.mSType = mScontextMatcher.group("stype");
+ mAuditLog.mSCategories = toCategories(mScontextMatcher.group("scategories"));
break;
case "tcontext":
- if (!nextTokenMatches(TCONTEXT_MATCHER)) {
+ if (!nextTokenMatches(mTcontextMatcher)) {
return null;
}
- mAuditLog.mTType = TCONTEXT_MATCHER.group("ttype");
- mAuditLog.mTCategories = toCategories(TCONTEXT_MATCHER.group("tcategories"));
+ mAuditLog.mTType = mTcontextMatcher.group("ttype");
+ mAuditLog.mTCategories = toCategories(mTcontextMatcher.group("tcategories"));
break;
case "tclass":
if (!mTokens.hasNext()) {
@@ -102,8 +133,8 @@
mAuditLog.mTClass = mTokens.next();
break;
case "path":
- if (nextTokenMatches(PATH_MATCHER)) {
- mAuditLog.mPath = PATH_MATCHER.group("path");
+ if (nextTokenMatches(mPathMatcher)) {
+ mAuditLog.mPath = mPathMatcher.group("path");
}
break;
case "permissive":
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
index 03822aa..c655d46 100644
--- a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
@@ -18,10 +18,12 @@
import android.util.EventLog;
import android.util.EventLog.Event;
import android.util.Log;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.selinux.SelinuxAuditLogBuilder.SelinuxAuditLog;
+import com.android.server.utils.Slogf;
import java.io.IOException;
import java.time.Instant;
@@ -37,6 +39,7 @@
class SelinuxAuditLogsCollector {
private static final String TAG = "SelinuxAuditLogs";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String SELINUX_PATTERN = "^.*\\bavc:\\s+(?<denial>.*)$";
@@ -48,13 +51,17 @@
@VisibleForTesting Instant mLastWrite = Instant.MIN;
- final AtomicBoolean mStopRequested = new AtomicBoolean(false);
+ AtomicBoolean mStopRequested = new AtomicBoolean(false);
SelinuxAuditLogsCollector(RateLimiter rateLimiter, QuotaLimiter quotaLimiter) {
mRateLimiter = rateLimiter;
mQuotaLimiter = quotaLimiter;
}
+ public void setStopRequested(boolean stopRequested) {
+ mStopRequested.set(stopRequested);
+ }
+
/**
* Collect and push SELinux audit logs for the provided {@code tagCode}.
*
@@ -66,7 +73,7 @@
boolean quotaExceeded = writeAuditLogs(logLines);
if (quotaExceeded) {
- Log.w(TAG, "Too many SELinux logs in the queue, I am giving up.");
+ Slog.w(TAG, "Too many SELinux logs in the queue, I am giving up.");
mLastWrite = latestTimestamp; // next run we will ignore all these logs.
logLines.clear();
}
@@ -79,7 +86,7 @@
try {
EventLog.readEvents(new int[] {tagCode}, events);
} catch (IOException e) {
- Log.e(TAG, "Error reading event logs", e);
+ Slog.e(TAG, "Error reading event logs", e);
}
Instant latestTimestamp = mLastWrite;
@@ -102,6 +109,7 @@
private boolean writeAuditLogs(Queue<Event> logLines) {
final SelinuxAuditLogBuilder auditLogBuilder = new SelinuxAuditLogBuilder();
+ int auditsWritten = 0;
while (!mStopRequested.get() && !logLines.isEmpty()) {
Event event = logLines.poll();
@@ -118,6 +126,9 @@
}
if (!mQuotaLimiter.acquire()) {
+ if (DEBUG) {
+ Slogf.d(TAG, "Running out of quota after %d logs.", auditsWritten);
+ }
return true;
}
mRateLimiter.acquire();
@@ -133,12 +144,16 @@
auditLog.mTClass,
auditLog.mPath,
auditLog.mPermissive);
+ auditsWritten++;
if (logTime.isAfter(mLastWrite)) {
mLastWrite = logTime;
}
}
+ if (DEBUG) {
+ Slogf.d(TAG, "Written %d logs", auditsWritten);
+ }
return false;
}
}
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java
new file mode 100644
index 0000000..0092c37
--- /dev/null
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.selinux;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.util.Slog;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This class handles the start and stop requests for the logs collector job, in particular making
+ * sure that at most one job is running at any given moment.
+ */
+final class SelinuxAuditLogsJob {
+
+ private static final String TAG = "SelinuxAuditLogs";
+
+ private final AtomicBoolean mIsRunning = new AtomicBoolean(false);
+ private final SelinuxAuditLogsCollector mAuditLogsCollector;
+
+ SelinuxAuditLogsJob(SelinuxAuditLogsCollector auditLogsCollector) {
+ mAuditLogsCollector = auditLogsCollector;
+ }
+
+ void requestStop() {
+ mAuditLogsCollector.mStopRequested.set(true);
+ }
+
+ boolean isRunning() {
+ return mIsRunning.get();
+ }
+
+ public void start(JobService jobService, JobParameters params) {
+ mAuditLogsCollector.mStopRequested.set(false);
+ if (mIsRunning.get()) {
+ Slog.i(TAG, "Selinux audit job is already running, ignore start request.");
+ return;
+ }
+ mIsRunning.set(true);
+ boolean done = mAuditLogsCollector.collect(SelinuxAuditLogsService.AUDITD_TAG_CODE);
+ if (done) {
+ jobService.jobFinished(params, /* wantsReschedule= */ false);
+ }
+ mIsRunning.set(false);
+ }
+}
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsService.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsService.java
index 8a661bc..d46e891 100644
--- a/services/core/java/com/android/server/selinux/SelinuxAuditLogsService.java
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsService.java
@@ -23,14 +23,16 @@
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
import android.util.EventLog;
-import android.util.Log;
+import android.util.Slog;
import java.time.Duration;
+import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
/**
* Scheduled jobs related to logging of SELinux denials and audits. The job runs daily on idle
@@ -43,58 +45,68 @@
static final int AUDITD_TAG_CODE = EventLog.getTagCode("auditd");
+ private static final String CONFIG_SELINUX_AUDIT_JOB_FREQUENCY_HOURS =
+ "selinux_audit_job_frequency_hours";
+ private static final String CONFIG_SELINUX_ENABLE_AUDIT_JOB = "selinux_enable_audit_job";
+ private static final String CONFIG_SELINUX_AUDIT_CAP = "selinux_audit_cap";
+ private static final int MAX_PERMITS_CAP_DEFAULT = 50000;
+
private static final int SELINUX_AUDIT_JOB_ID = 25327386;
- private static final JobInfo SELINUX_AUDIT_JOB =
- new JobInfo.Builder(
- SELINUX_AUDIT_JOB_ID,
- new ComponentName("android", SelinuxAuditLogsService.class.getName()))
- .setPeriodic(TimeUnit.DAYS.toMillis(1))
- .setRequiresDeviceIdle(true)
- .setRequiresCharging(true)
- .setRequiresBatteryNotLow(true)
- .build();
+ private static final ComponentName SELINUX_AUDIT_JOB_COMPONENT =
+ new ComponentName("android", SelinuxAuditLogsService.class.getName());
private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
- private static final AtomicReference<Boolean> IS_RUNNING = new AtomicReference<>(false);
- // Audit logging is subject to both rate and quota limiting. We can only push one atom every 10
- // milliseconds, and no more than 50K atoms can be pushed each day.
- private static final SelinuxAuditLogsCollector AUDIT_LOGS_COLLECTOR =
- new SelinuxAuditLogsCollector(
- new RateLimiter(/* window= */ Duration.ofMillis(10)),
- new QuotaLimiter(/* maxPermitsPerDay= */ 50000));
+ // Audit logging is subject to both rate and quota limiting. A {@link RateLimiter} makes sure
+ // that we push no more than one atom every 10 milliseconds. A {@link QuotaLimiter} caps the
+ // number of atoms pushed per day to CONFIG_SELINUX_AUDIT_CAP. The quota limiter is static
+ // because new job executions happen in a new instance of this class. Making the quota limiter
+ // an instance reference would reset the quota limitations between jobs executions.
+ private static final Duration RATE_LIMITER_WINDOW = Duration.ofMillis(10);
+ private static final QuotaLimiter QUOTA_LIMITER =
+ new QuotaLimiter(
+ DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ CONFIG_SELINUX_AUDIT_CAP,
+ MAX_PERMITS_CAP_DEFAULT));
+ private static final SelinuxAuditLogsJob LOGS_COLLECTOR_JOB =
+ new SelinuxAuditLogsJob(
+ new SelinuxAuditLogsCollector(
+ new RateLimiter(RATE_LIMITER_WINDOW), QUOTA_LIMITER));
/** Schedule jobs with the {@link JobScheduler}. */
public static void schedule(Context context) {
if (!selinuxSdkSandboxAudit()) {
- Log.d(TAG, "SelinuxAuditLogsService not enabled");
+ Slog.d(TAG, "SelinuxAuditLogsService not enabled");
return;
}
if (AUDITD_TAG_CODE == -1) {
- Log.e(TAG, "auditd is not a registered tag on this system");
+ Slog.e(TAG, "auditd is not a registered tag on this system");
return;
}
- if (context.getSystemService(JobScheduler.class)
- .forNamespace(SELINUX_AUDIT_NAMESPACE)
- .schedule(SELINUX_AUDIT_JOB)
- == JobScheduler.RESULT_FAILURE) {
- Log.e(TAG, "SelinuxAuditLogsService could not be started.");
- }
+ LogsCollectorJobScheduler propertiesListener =
+ new LogsCollectorJobScheduler(
+ context.getSystemService(JobScheduler.class)
+ .forNamespace(SELINUX_AUDIT_NAMESPACE));
+ propertiesListener.schedule();
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_ADSERVICES, context.getMainExecutor(), propertiesListener);
}
@Override
public boolean onStartJob(JobParameters params) {
if (params.getJobId() != SELINUX_AUDIT_JOB_ID) {
- Log.e(TAG, "The job id does not match the expected selinux job id.");
+ Slog.e(TAG, "The job id does not match the expected selinux job id.");
+ return false;
+ }
+ if (!selinuxSdkSandboxAudit()) {
+ Slog.i(TAG, "Selinux audit job disabled.");
return false;
}
- AUDIT_LOGS_COLLECTOR.mStopRequested.set(false);
- IS_RUNNING.set(true);
- EXECUTOR_SERVICE.execute(new LogsCollectorJob(this, params));
-
+ EXECUTOR_SERVICE.execute(() -> LOGS_COLLECTOR_JOB.start(this, params));
return true; // the job is running
}
@@ -104,29 +116,69 @@
return false;
}
- AUDIT_LOGS_COLLECTOR.mStopRequested.set(true);
- return IS_RUNNING.get();
+ if (LOGS_COLLECTOR_JOB.isRunning()) {
+ LOGS_COLLECTOR_JOB.requestStop();
+ return true;
+ }
+ return false;
}
- private static class LogsCollectorJob implements Runnable {
- private final JobService mAuditLogService;
- private final JobParameters mParams;
+ /**
+ * This class is in charge of scheduling the job service, and keeping the scheduling up to date
+ * when the parameters change.
+ */
+ private static final class LogsCollectorJobScheduler
+ implements DeviceConfig.OnPropertiesChangedListener {
- LogsCollectorJob(JobService auditLogService, JobParameters params) {
- mAuditLogService = auditLogService;
- mParams = params;
+ private final JobScheduler mJobScheduler;
+
+ private LogsCollectorJobScheduler(JobScheduler jobScheduler) {
+ mJobScheduler = jobScheduler;
}
@Override
- public void run() {
- IS_RUNNING.updateAndGet(
- isRunning -> {
- boolean done = AUDIT_LOGS_COLLECTOR.collect(AUDITD_TAG_CODE);
- if (done) {
- mAuditLogService.jobFinished(mParams, /* wantsReschedule= */ false);
- }
- return !done;
- });
+ public void onPropertiesChanged(Properties changedProperties) {
+ Set<String> keyset = changedProperties.getKeyset();
+
+ if (keyset.contains(CONFIG_SELINUX_AUDIT_CAP)) {
+ QUOTA_LIMITER.setMaxPermits(
+ changedProperties.getInt(
+ CONFIG_SELINUX_AUDIT_CAP, MAX_PERMITS_CAP_DEFAULT));
+ }
+
+ if (keyset.contains(CONFIG_SELINUX_ENABLE_AUDIT_JOB)) {
+ boolean enabled =
+ changedProperties.getBoolean(
+ CONFIG_SELINUX_ENABLE_AUDIT_JOB, /* defaultValue= */ false);
+ if (enabled) {
+ schedule();
+ } else {
+ mJobScheduler.cancel(SELINUX_AUDIT_JOB_ID);
+ }
+ } else if (keyset.contains(CONFIG_SELINUX_AUDIT_JOB_FREQUENCY_HOURS)) {
+ // The job frequency changed, reschedule.
+ schedule();
+ }
+ }
+
+ private void schedule() {
+ long frequencyMillis =
+ TimeUnit.HOURS.toMillis(
+ DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ CONFIG_SELINUX_AUDIT_JOB_FREQUENCY_HOURS,
+ 24));
+ if (mJobScheduler.schedule(
+ new JobInfo.Builder(SELINUX_AUDIT_JOB_ID, SELINUX_AUDIT_JOB_COMPONENT)
+ .setPeriodic(frequencyMillis)
+ .setRequiresDeviceIdle(true)
+ .setRequiresBatteryNotLow(true)
+ .build())
+ == JobScheduler.RESULT_FAILURE) {
+ Slog.e(TAG, "SelinuxAuditLogsService could not be scheduled.");
+ } else {
+ Slog.d(TAG, "SelinuxAuditLogsService scheduled successfully.");
+ }
}
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 2ff3861..e4f60ec 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -22,6 +22,7 @@
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.UserHandle;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -248,10 +249,10 @@
/**
* Shows the media output switcher dialog.
*
- * @param packageName of the session for which the output switcher is shown.
+ * @param targetPackageName of the session for which the output switcher is shown.
* @see com.android.internal.statusbar.IStatusBar#showMediaOutputSwitcher
*/
- void showMediaOutputSwitcher(String packageName);
+ void showMediaOutputSwitcher(String targetPackageName, UserHandle targetUserHandle);
/**
* Add a tile to the Quick Settings Panel
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index cca5beb..2c67207 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -850,11 +850,11 @@
}
@Override
- public void showMediaOutputSwitcher(String packageName) {
+ public void showMediaOutputSwitcher(String targetPackageName, UserHandle targetUserHandle) {
IStatusBar bar = mBar;
if (bar != null) {
try {
- bar.showMediaOutputSwitcher(packageName);
+ bar.showMediaOutputSwitcher(targetPackageName, targetUserHandle);
} catch (RemoteException ex) {
}
}
diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java
index 58c31d5..ad2c3e8 100644
--- a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java
+++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java
@@ -39,6 +39,9 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IUserRestrictionsListener;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -136,9 +139,11 @@
}
}, filter, null, null /* main thread */);
+ Handler mainThreadHandler = mContext.getMainThreadHandler();
+
// Add async callbacks for global settings being changed.
ContentResolver contentResolver = mContext.getContentResolver();
- ContentObserver contentObserver = new ContentObserver(mContext.getMainThreadHandler()) {
+ ContentObserver contentObserver = new ContentObserver(mainThreadHandler) {
@Override
public void onChange(boolean selfChange) {
handleConfigurationInternalChangeOnMainThread();
@@ -150,6 +155,20 @@
// Watch server flags.
mServerFlags.addListener(this::handleConfigurationInternalChangeOnMainThread,
SERVER_FLAGS_KEYS_TO_WATCH);
+
+ // Watch for policy changes that affect what the user is permitted to do.
+ mUserManager.addUserRestrictionsListener(
+ new IUserRestrictionsListener.Stub() {
+ @Override
+ public void onUserRestrictionsChanged(
+ int userId, Bundle newRestrictions, Bundle prevRestrictions) {
+ // This callback currently delivered on main thread, but this post() is
+ // defensive and doesn't rely on that in case it changes.
+ mainThreadHandler.post(
+ () -> handleUserRestrictionsChangeOnMainThread(
+ userId, newRestrictions, prevRestrictions));
+ }
+ });
}
/** Returns the singleton instance. */
@@ -174,6 +193,13 @@
}
}
+ private void handleUserRestrictionsChangeOnMainThread(
+ int userId, Bundle newRestrictions, Bundle prevRestrictions) {
+ // No attempt at optimisation here. If the policy changes in any way for any user, just
+ // notify.
+ handleConfigurationInternalChangeOnMainThread();
+ }
+
@Override
public synchronized void addConfigurationInternalChangeListener(
@NonNull StateChangeListener listener) {
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
index a71f9c7..40353a2 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
@@ -32,6 +32,9 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IUserRestrictionsListener;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -175,9 +178,11 @@
}
}, filter, null, null /* main thread */);
+ Handler mainThreadHandler = mContext.getMainThreadHandler();
+
// Add async callbacks for changes to global settings that influence behavior.
ContentResolver contentResolver = mContext.getContentResolver();
- ContentObserver contentObserver = new ContentObserver(mContext.getMainThreadHandler()) {
+ ContentObserver contentObserver = new ContentObserver(mainThreadHandler) {
@Override
public void onChange(boolean selfChange) {
handleConfigurationInternalChangeOnMainThread();
@@ -197,6 +202,20 @@
// Watch server flags.
mServerFlags.addListener(this::handleConfigurationInternalChangeOnMainThread,
CONFIGURATION_INTERNAL_SERVER_FLAGS_KEYS_TO_WATCH);
+
+ // Watch for policy changes that affect what the user is permitted to do.
+ mUserManager.addUserRestrictionsListener(
+ new IUserRestrictionsListener.Stub() {
+ @Override
+ public void onUserRestrictionsChanged(
+ int userId, Bundle newRestrictions, Bundle prevRestrictions) {
+ // This callback currently delivered on main thread, but this post() is
+ // defensive and doesn't rely on that in case it changes.
+ mainThreadHandler.post(
+ () -> handleUserRestrictionsChangeOnMainThread(
+ userId, newRestrictions, prevRestrictions));
+ }
+ });
}
/** Returns the singleton instance. */
@@ -221,6 +240,13 @@
}
}
+ private void handleUserRestrictionsChangeOnMainThread(
+ int userId, Bundle newRestrictions, Bundle prevRestrictions) {
+ // No attempt at optimisation here. If the policy changes in any way for any user, just
+ // notify.
+ handleConfigurationInternalChangeOnMainThread();
+ }
+
@Override
public synchronized void addConfigurationInternalChangeListener(
@NonNull StateChangeListener listener) {
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
index 10e868d..c1d92cf 100644
--- a/services/core/java/com/android/server/tracing/TracingServiceProxy.java
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -119,8 +119,13 @@
}
@Override
- public void onStart() {
- publishBinderService(TRACING_SERVICE_PROXY_BINDER_NAME, mTracingServiceProxy);
+ public void onStart() {}
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+ publishBinderService(TRACING_SERVICE_PROXY_BINDER_NAME, mTracingServiceProxy);
+ }
}
private void notifyTraceur(boolean sessionStolen) {
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index ed9fa65..3619253 100644
--- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -16,8 +16,10 @@
package com.android.server.vcn.routeselection;
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -38,6 +40,10 @@
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.server.vcn.VcnContext;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.BitSet;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@@ -56,8 +62,51 @@
public class IpSecPacketLossDetector extends NetworkMetricMonitor {
private static final String TAG = IpSecPacketLossDetector.class.getSimpleName();
+ private static final int PACKET_LOSS_PERCENT_UNAVAILABLE = -1;
+
+ // Ignore the packet loss detection result if the expected packet number is smaller than 10.
+ // Solarwinds NPM uses 10 ICMP echos to calculate packet loss rate (as per
+ // https://thwack.solarwinds.com/products/network-performance-monitor-npm/f/forum/63829/how-is-packet-loss-calculated)
@VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PACKET_LOSS_UNAVALAIBLE = -1;
+ static final int MIN_VALID_EXPECTED_RX_PACKET_NUM = 10;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"PACKET_LOSS_"},
+ value = {
+ PACKET_LOSS_RATE_VALID,
+ PACKET_LOSS_RATE_INVALID,
+ PACKET_LOSS_UNUSUAL_SEQ_NUM_LEAP,
+ })
+ @Target({ElementType.TYPE_USE})
+ private @interface PacketLossResultType {}
+
+ /** Indicates a valid packet loss rate is available */
+ private static final int PACKET_LOSS_RATE_VALID = 0;
+
+ /**
+ * Indicates that the detector cannot get a valid packet loss rate due to one of the following
+ * reasons:
+ *
+ * <ul>
+ * <li>The replay window did not proceed and thus all packets might have been delivered out of
+ * order
+ * <li>The expected received packet number is too small and thus the detection result is not
+ * reliable
+ * <li>There are unexpected errors
+ * </ul>
+ */
+ private static final int PACKET_LOSS_RATE_INVALID = 1;
+
+ /**
+ * The sequence number increase is unusually large and might be caused an intentional leap on
+ * the server's downlink
+ *
+ * <p>Inbound sequence number will not always increase consecutively. During load balancing the
+ * server might add a big leap on the sequence number intentionally. In such case a high packet
+ * loss rate does not always indicate a lossy network
+ */
+ private static final int PACKET_LOSS_UNUSUAL_SEQ_NUM_LEAP = 2;
// For VoIP, losses between 5% and 10% of the total packet stream will affect the quality
// significantly (as per "Computer Networking for LANS to WANS: Hardware, Software and
@@ -68,8 +117,12 @@
private static final int POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT = 20;
+ // By default, there's no maximum limit enforced
+ private static final int MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED = -1;
+
private long mPollIpSecStateIntervalMs;
- private final int mPacketLossRatePercentThreshold;
+ private int mPacketLossRatePercentThreshold;
+ private int mMaxSeqNumIncreasePerSecond;
@NonNull private final Handler mHandler;
@NonNull private final PowerManager mPowerManager;
@@ -108,6 +161,7 @@
mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig);
mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig);
+ mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig);
// Register for system broadcasts to monitor idle mode change
final IntentFilter intentFilter = new IntentFilter();
@@ -172,6 +226,24 @@
return IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT;
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static int getMaxSeqNumIncreasePerSecond(@Nullable PersistableBundleWrapper carrierConfig) {
+ int maxSeqNumIncrease = MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED;
+ if (Flags.handleSeqNumLeap() && carrierConfig != null) {
+ maxSeqNumIncrease =
+ carrierConfig.getInt(
+ VcnManager.VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY,
+ MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED);
+ }
+
+ if (maxSeqNumIncrease < MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED) {
+ logE(TAG, "Invalid value of MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY " + maxSeqNumIncrease);
+ return MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED;
+ }
+
+ return maxSeqNumIncrease;
+ }
+
@Override
protected void onSelectedUnderlyingNetworkChanged() {
if (!isSelectedUnderlyingNetwork()) {
@@ -207,6 +279,11 @@
// The already scheduled event will not be affected. The followup events will be scheduled
// with the new interval
mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig);
+
+ if (Flags.handleSeqNumLeap()) {
+ mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig);
+ mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig);
+ }
}
@Override
@@ -307,30 +384,40 @@
return;
}
- final int packetLossRate =
+ final PacketLossCalculationResult calculateResult =
mPacketLossCalculator.getPacketLossRatePercentage(
- mLastIpSecTransformState, state, getLogPrefix());
+ mLastIpSecTransformState,
+ state,
+ mMaxSeqNumIncreasePerSecond,
+ getLogPrefix());
- if (packetLossRate == PACKET_LOSS_UNAVALAIBLE) {
+ if (calculateResult.getResultType() == PACKET_LOSS_RATE_INVALID) {
return;
}
final String logMsg =
- "packetLossRate: "
- + packetLossRate
+ "calculateResult: "
+ + calculateResult
+ "% in the past "
+ (state.getTimestampMillis()
- mLastIpSecTransformState.getTimestampMillis())
+ "ms";
mLastIpSecTransformState = state;
- if (packetLossRate < mPacketLossRatePercentThreshold) {
+ if (calculateResult.getPacketLossRatePercent() < mPacketLossRatePercentThreshold) {
logV(logMsg);
+
+ // In both "valid" or "unusual_seq_num_leap" cases, notify that the network has passed
+ // the validation
onValidationResultReceivedInternal(false /* isFailed */);
} else {
logInfo(logMsg);
- onValidationResultReceivedInternal(true /* isFailed */);
+ if (calculateResult.getResultType() == PACKET_LOSS_RATE_VALID) {
+ onValidationResultReceivedInternal(true /* isFailed */);
+ }
+
+ // In both "valid" or "unusual_seq_num_leap" cases, trigger network validation
if (Flags.validateNetworkOnIpsecLoss()) {
// Trigger re-validation of the underlying network; if it fails, the VCN will
// attempt to migrate away.
@@ -343,9 +430,10 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class PacketLossCalculator {
/** Calculate the packet loss rate between two timestamps */
- public int getPacketLossRatePercentage(
+ public PacketLossCalculationResult getPacketLossRatePercentage(
@NonNull IpSecTransformState oldState,
@NonNull IpSecTransformState newState,
+ int maxSeqNumIncreasePerSecond,
String logPrefix) {
logVIpSecTransform("oldState", oldState, logPrefix);
logVIpSecTransform("newState", newState, logPrefix);
@@ -359,7 +447,23 @@
if (oldSeqHi == newSeqHi || newSeqHi < replayWindowSize) {
// The replay window did not proceed and all packets might have been delivered out
// of order
- return PACKET_LOSS_UNAVALAIBLE;
+ return PacketLossCalculationResult.invalid();
+ }
+
+ boolean isUnusualSeqNumLeap = false;
+
+ // Handle sequence number leap
+ if (Flags.handleSeqNumLeap()
+ && maxSeqNumIncreasePerSecond != MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED) {
+ final long timeDiffMillis =
+ newState.getTimestampMillis() - oldState.getTimestampMillis();
+ final long maxSeqNumIncrease = timeDiffMillis * maxSeqNumIncreasePerSecond / 1000;
+
+ // Sequence numbers are unsigned 32-bit values. If maxSeqNumIncrease overflows,
+ // isUnusualSeqNumLeap can never be true.
+ if (maxSeqNumIncrease >= 0 && newSeqHi - oldSeqHi >= maxSeqNumIncrease) {
+ isUnusualSeqNumLeap = true;
+ }
}
// Get the expected packet count by assuming there is no packet loss. In this case, SA
@@ -381,15 +485,23 @@
+ " actualPktCntDiff: "
+ actualPktCntDiff);
+ if (Flags.handleSeqNumLeap() && expectedPktCntDiff < MIN_VALID_EXPECTED_RX_PACKET_NUM) {
+ // The sample size is too small to ensure a reliable detection result
+ return PacketLossCalculationResult.invalid();
+ }
+
if (expectedPktCntDiff < 0
|| expectedPktCntDiff == 0
|| actualPktCntDiff < 0
|| actualPktCntDiff > expectedPktCntDiff) {
logWtf(TAG, "Impossible values for expectedPktCntDiff or" + " actualPktCntDiff");
- return PACKET_LOSS_UNAVALAIBLE;
+ return PacketLossCalculationResult.invalid();
}
- return 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff);
+ final int percent = 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff);
+ return isUnusualSeqNumLeap
+ ? PacketLossCalculationResult.unusualSeqNumLeap(percent)
+ : PacketLossCalculationResult.valid(percent);
}
}
@@ -409,4 +521,64 @@
private static long getPacketCntInReplayWindow(@NonNull IpSecTransformState state) {
return BitSet.valueOf(state.getReplayBitmap()).cardinality();
}
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class PacketLossCalculationResult {
+ @PacketLossResultType private final int mResultType;
+ private final int mPacketLossRatePercent;
+
+ private PacketLossCalculationResult(@PacketLossResultType int type, int percent) {
+ mResultType = type;
+ mPacketLossRatePercent = percent;
+ }
+
+ /** Construct an instance that contains a valid packet loss rate */
+ public static PacketLossCalculationResult valid(int percent) {
+ return new PacketLossCalculationResult(PACKET_LOSS_RATE_VALID, percent);
+ }
+
+ /** Construct an instance indicating the inability to get a valid packet loss rate */
+ public static PacketLossCalculationResult invalid() {
+ return new PacketLossCalculationResult(
+ PACKET_LOSS_RATE_INVALID, PACKET_LOSS_PERCENT_UNAVAILABLE);
+ }
+
+ /** Construct an instance indicating that there is an unusual sequence number leap */
+ public static PacketLossCalculationResult unusualSeqNumLeap(int percent) {
+ return new PacketLossCalculationResult(PACKET_LOSS_UNUSUAL_SEQ_NUM_LEAP, percent);
+ }
+
+ @PacketLossResultType
+ public int getResultType() {
+ return mResultType;
+ }
+
+ public int getPacketLossRatePercent() {
+ return mPacketLossRatePercent;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mResultType, mPacketLossRatePercent);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof PacketLossCalculationResult)) {
+ return false;
+ }
+
+ final PacketLossCalculationResult rhs = (PacketLossCalculationResult) other;
+ return mResultType == rhs.mResultType
+ && mPacketLossRatePercent == rhs.mPacketLossRatePercent;
+ }
+
+ @Override
+ public String toString() {
+ return "mResultType: "
+ + mResultType
+ + " | mPacketLossRatePercent: "
+ + mPacketLossRatePercent;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
index a1b212f..b9b1060 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
@@ -272,6 +272,11 @@
}
}
+ protected static void logE(String className, String msgWithPrefix) {
+ Slog.w(className, msgWithPrefix);
+ LOCAL_LOG.log("[ERROR ] " + className + msgWithPrefix);
+ }
+
protected static void logWtf(String className, String msgWithPrefix) {
Slog.wtf(className, msgWithPrefix);
LOCAL_LOG.log("[WTF ] " + className + msgWithPrefix);
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index 96f045d..8138168 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -44,6 +44,8 @@
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+ private static final VibrationAttributes COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST);
private final VibratorInfo mVibratorInfo;
private final boolean mHapticTextHandleEnabled;
@@ -120,7 +122,6 @@
return getKeyboardVibration(effectId);
case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
- case HapticFeedbackConstants.ENTRY_BUMP:
case HapticFeedbackConstants.DRAG_CROSSING:
return getVibration(
effectId,
@@ -131,6 +132,7 @@
case HapticFeedbackConstants.EDGE_RELEASE:
case HapticFeedbackConstants.CALENDAR_DATE:
case HapticFeedbackConstants.CONFIRM:
+ case HapticFeedbackConstants.BIOMETRIC_CONFIRM:
case HapticFeedbackConstants.GESTURE_START:
case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
case HapticFeedbackConstants.SCROLL_LIMIT:
@@ -143,6 +145,7 @@
return getVibration(effectId, VibrationEffect.EFFECT_HEAVY_CLICK);
case HapticFeedbackConstants.REJECT:
+ case HapticFeedbackConstants.BIOMETRIC_REJECT:
return getVibration(effectId, VibrationEffect.EFFECT_DOUBLE_CLICK);
case HapticFeedbackConstants.SAFE_MODE_ENABLED:
@@ -207,6 +210,10 @@
case HapticFeedbackConstants.KEYBOARD_RELEASE:
attrs = createKeyboardVibrationAttributes(fromIme);
break;
+ case HapticFeedbackConstants.BIOMETRIC_CONFIRM:
+ case HapticFeedbackConstants.BIOMETRIC_REJECT:
+ attrs = COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES;
+ break;
default:
attrs = TOUCH_VIBRATION_ATTRIBUTES;
}
@@ -225,6 +232,23 @@
return flags == 0 ? attrs : new VibrationAttributes.Builder(attrs).setFlags(flags).build();
}
+ /**
+ * Returns true if given haptic feedback is restricted to system apps with permission
+ * {@code android.permission.VIBRATE_SYSTEM_CONSTANTS}.
+ *
+ * @param effectId the haptic feedback effect ID to check.
+ * @return true if the haptic feedback is restricted, false otherwise.
+ */
+ public boolean isRestrictedHapticFeedback(int effectId) {
+ switch (effectId) {
+ case HapticFeedbackConstants.BIOMETRIC_CONFIRM:
+ case HapticFeedbackConstants.BIOMETRIC_REJECT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
/** Dumps relevant state. */
public void dump(String prefix, PrintWriter pw) {
pw.print("mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
index b33fa6f..f82ff67 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControlService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -101,7 +101,9 @@
}
@Override
- public void registerVibratorController(IVibratorController controller) {
+ public void registerVibratorController(@NonNull IVibratorController controller) {
+ Objects.requireNonNull(controller);
+
synchronized (mLock) {
mVibratorControllerHolder.setVibratorController(controller);
}
@@ -134,6 +136,7 @@
public void setVibrationParams(@SuppressLint("ArrayReturn") VibrationParam[] params,
@NonNull IVibratorController token) {
Objects.requireNonNull(token);
+ requireContainsNoNullElement(params);
synchronized (mLock) {
if (mVibratorControllerHolder.getVibratorController() == null) {
@@ -148,6 +151,13 @@
+ "controller doesn't match the registered one. " + this);
return;
}
+ if (params == null) {
+ // Adaptive haptics scales cannot be set to null. Ignoring request.
+ Slog.d(TAG,
+ "New vibration params received but are null. New vibration "
+ + "params ignored.");
+ return;
+ }
updateAdaptiveHapticsScales(params);
recordUpdateVibrationParams(params, /* fromRequest= */ false);
@@ -181,6 +191,7 @@
public void onRequestVibrationParamsComplete(
@NonNull IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result) {
Objects.requireNonNull(requestToken);
+ requireContainsNoNullElement(result);
synchronized (mLock) {
if (mVibrationParamRequest == null) {
@@ -202,6 +213,13 @@
long latencyMs = SystemClock.uptimeMillis() - mVibrationParamRequest.uptimeMs;
mStatsLogger.logVibrationParamRequestLatency(mVibrationParamRequest.uid, latencyMs);
+ if (result == null) {
+ Slog.d(TAG,
+ "New vibration params received but are null. New vibration "
+ + "params ignored.");
+ return;
+ }
+
updateAdaptiveHapticsScales(result);
endOngoingRequestVibrationParamsLocked(/* wasCancelled= */ false);
recordUpdateVibrationParams(result, /* fromRequest= */ true);
@@ -401,10 +419,9 @@
*
* @param params the new vibration params.
*/
- private void updateAdaptiveHapticsScales(@Nullable VibrationParam[] params) {
- if (params == null) {
- return;
- }
+ private void updateAdaptiveHapticsScales(@NonNull VibrationParam[] params) {
+ Objects.requireNonNull(params);
+
for (VibrationParam param : params) {
if (param.getTag() != VibrationParam.scale) {
Slog.e(TAG, "Unsupported vibration param: " + param);
@@ -448,11 +465,10 @@
mVibrationScaler.updateAdaptiveHapticsScale(usageHint, scale);
}
- private void recordUpdateVibrationParams(@Nullable VibrationParam[] params,
+ private void recordUpdateVibrationParams(@NonNull VibrationParam[] params,
boolean fromRequest) {
- if (params == null) {
- return;
- }
+ Objects.requireNonNull(params);
+
VibrationParamsRecords.Operation operation =
fromRequest ? VibrationParamsRecords.Operation.PULL
: VibrationParamsRecords.Operation.PUSH;
@@ -474,6 +490,13 @@
VibrationParamsRecords.Operation.CLEAR, createTime, typesMask, NO_SCALE));
}
+ private void requireContainsNoNullElement(VibrationParam[] params) {
+ if (ArrayUtils.contains(params, null)) {
+ throw new IllegalArgumentException(
+ "Invalid vibration params received: null values are not permitted.");
+ }
+ }
+
/**
* Keep records of {@link VibrationParam} values received by this service from a registered
* {@link VibratorController} and provide debug information for this service.
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 9e9025e..8281ac1 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -439,6 +439,11 @@
Slog.w(TAG, "performHapticFeedback; haptic vibration provider not ready.");
return null;
}
+ if (hapticVibrationProvider.isRestrictedHapticFeedback(constant)
+ && !hasPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS)) {
+ Slog.w(TAG, "performHapticFeedback; no permission for effect " + constant);
+ return null;
+ }
VibrationEffect effect = hapticVibrationProvider.getVibrationForHapticFeedback(constant);
if (effect == null) {
Slog.w(TAG, "performHapticFeedback; vibration absent for effect " + constant);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index 6905802..f6e0168 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -137,6 +137,27 @@
// This will fall into "Case 4" of this function and center the folded screen
return getCrop(displaySize, bitmapSize, newSuggestedCrops, rtl);
}
+
+ // The second exception is if we're on tablet and we're on portrait mode.
+ // In that case, center the wallpaper relatively to landscape and put some parallax.
+ boolean isTablet = mWallpaperDisplayHelper.isLargeScreen()
+ && !mWallpaperDisplayHelper.isFoldable();
+ if (isTablet && displaySize.x < displaySize.y) {
+ Point rotatedDisplaySize = new Point(displaySize.y, displaySize.x);
+ // compute the crop on landscape (without parallax)
+ Rect landscapeCrop = getCrop(rotatedDisplaySize, bitmapSize, suggestedCrops, rtl);
+ landscapeCrop = noParallax(landscapeCrop, rotatedDisplaySize, bitmapSize, rtl);
+ // compute the crop on portrait at the center of the landscape crop
+ crop = getAdjustedCrop(landscapeCrop, bitmapSize, displaySize, false, rtl, ADD);
+
+ // add some parallax (until the border of the landscape crop without parallax)
+ if (rtl) {
+ crop.left = landscapeCrop.left;
+ } else {
+ crop.right = landscapeCrop.right;
+ }
+ }
+
return getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD);
}
@@ -472,6 +493,7 @@
}
}
final Rect cropHint;
+ final SparseArray<Rect> defaultCrops;
// A wallpaper with cropHints = Map.of(ORIENTATION_UNKNOWN, rect) is treated like
// a wallpaper with cropHints = null and cropHint = rect.
@@ -483,7 +505,7 @@
if (multiCrop() && wallpaper.mCropHints.size() > 0) {
// Some suggested crops per screen orientation were provided,
// use them to compute the default crops for this device
- SparseArray<Rect> defaultCrops = getDefaultCrops(wallpaper.mCropHints, bitmapSize);
+ defaultCrops = getDefaultCrops(wallpaper.mCropHints, bitmapSize);
// Adapt the provided crops to match the actual crops for the default display
SparseArray<Rect> updatedCropHints = new SparseArray<>();
for (int i = 0; i < wallpaper.mCropHints.size(); i++) {
@@ -514,7 +536,7 @@
wallpaper.cropHint.set(bitmapRect);
}
Point cropSize = new Point(wallpaper.cropHint.width(), wallpaper.cropHint.height());
- SparseArray<Rect> defaultCrops = getDefaultCrops(new SparseArray<>(), cropSize);
+ defaultCrops = getDefaultCrops(new SparseArray<>(), cropSize);
cropHint = getTotalCrop(defaultCrops);
cropHint.offset(wallpaper.cropHint.left, wallpaper.cropHint.top);
wallpaper.cropHint.set(cropHint);
@@ -523,6 +545,7 @@
}
} else {
cropHint = new Rect(wallpaper.cropHint);
+ defaultCrops = null;
}
if (DEBUG) {
@@ -563,6 +586,33 @@
|| cropHint.height() > GLHelper.getMaxTextureSize()
|| cropHint.width() > GLHelper.getMaxTextureSize();
+ float sampleSize = Float.MAX_VALUE;
+ if (multiCrop()) {
+ // If all crops for all orientations have more width and height in pixel
+ // than the display for this orientation, downsample the image
+ for (int i = 0; i < defaultCrops.size(); i++) {
+ int orientation = defaultCrops.keyAt(i);
+ Rect crop = defaultCrops.valueAt(i);
+ Point displayForThisOrientation = mWallpaperDisplayHelper
+ .getDefaultDisplaySizes().get(orientation);
+ if (displayForThisOrientation == null) continue;
+ float sampleSizeForThisOrientation = Math.max(1f, Math.min(
+ crop.width() / displayForThisOrientation.x,
+ crop.height() / displayForThisOrientation.y));
+ sampleSize = Math.min(sampleSize, sampleSizeForThisOrientation);
+ }
+ // If the total crop has more width or height than either the max texture size
+ // or twice the largest display dimension, downsample the image
+ int maxCropSize = Math.min(
+ 2 * mWallpaperDisplayHelper.getDefaultDisplayLargestDimension(),
+ GLHelper.getMaxTextureSize());
+ float minimumSampleSize = Math.max(1f, Math.max(
+ (float) cropHint.height() / maxCropSize,
+ (float) cropHint.width()) / maxCropSize);
+ sampleSize = Math.max(sampleSize, minimumSampleSize);
+ needScale = sampleSize > 1f;
+ }
+
//make sure screen aspect ratio is preserved if width is scaled under screen size
if (needScale && !multiCrop()) {
final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height();
@@ -577,7 +627,8 @@
if (DEBUG_CROP) {
Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
- Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
+ if (multiCrop()) Slog.v(TAG, "defaultCrops: " + defaultCrops);
+ if (!multiCrop()) Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
}
@@ -620,28 +671,17 @@
options.inJustDecodeBounds = false;
final Rect estimateCrop = new Rect(cropHint);
- estimateCrop.scale(1f / options.inSampleSize);
+ if (!multiCrop()) estimateCrop.scale(1f / options.inSampleSize);
+ else estimateCrop.scale(1f / sampleSize);
float hRatio = (float) wpData.mHeight / estimateCrop.height();
- if (multiCrop()) {
- // make sure the crop height is at most the display largest dimension
- hRatio = (float) mWallpaperDisplayHelper.getDefaultDisplayLargestDimension()
- / estimateCrop.height();
- hRatio = Math.min(hRatio, 1f);
- }
final int destHeight = (int) (estimateCrop.height() * hRatio);
final int destWidth = (int) (estimateCrop.width() * hRatio);
// We estimated an invalid crop, try to adjust the cropHint to get a valid one.
- if (destWidth > GLHelper.getMaxTextureSize()) {
+ if (!multiCrop() && destWidth > GLHelper.getMaxTextureSize()) {
if (DEBUG) {
Slog.w(TAG, "Invalid crop dimensions, trying to adjust.");
}
- if (multiCrop()) {
- // clear custom crop guidelines, fallback to system default
- wallpaper.mCropHints.clear();
- generateCropInternal(wallpaper);
- return;
- }
int newHeight = (int) (wpData.mHeight / hRatio);
int newWidth = (int) (wpData.mWidth / hRatio);
@@ -658,16 +698,27 @@
// We've got the safe cropHint; now we want to scale it properly to
// the desired rectangle.
// That's a height-biased operation: make it fit the hinted height.
- final int safeHeight = (int) (estimateCrop.height() * hRatio + 0.5f);
- final int safeWidth = (int) (estimateCrop.width() * hRatio + 0.5f);
+ final int safeHeight = !multiCrop()
+ ? (int) (estimateCrop.height() * hRatio + 0.5f)
+ : (int) (cropHint.height() / sampleSize + 0.5f);
+ final int safeWidth = !multiCrop()
+ ? (int) (estimateCrop.width() * hRatio + 0.5f)
+ : (int) (cropHint.width() / sampleSize + 0.5f);
if (DEBUG_CROP) {
Slog.v(TAG, "Decode parameters:");
- Slog.v(TAG, " cropHint=" + cropHint + ", estimateCrop=" + estimateCrop);
- Slog.v(TAG, " down sampling=" + options.inSampleSize
- + ", hRatio=" + hRatio);
- Slog.v(TAG, " dest=" + destWidth + "x" + destHeight);
- Slog.v(TAG, " safe=" + safeWidth + "x" + safeHeight);
+ if (!multiCrop()) {
+ Slog.v(TAG,
+ " cropHint=" + cropHint + ", estimateCrop=" + estimateCrop);
+ Slog.v(TAG, " down sampling=" + options.inSampleSize
+ + ", hRatio=" + hRatio);
+ Slog.v(TAG, " dest=" + destWidth + "x" + destHeight);
+ }
+ if (multiCrop()) {
+ Slog.v(TAG, " cropHint=" + cropHint);
+ Slog.v(TAG, " sampleSize=" + sampleSize);
+ }
+ Slog.v(TAG, " targetSize=" + safeWidth + "x" + safeHeight);
Slog.v(TAG, " maxTextureSize=" + GLHelper.getMaxTextureSize());
}
@@ -682,24 +733,28 @@
final ImageDecoder.Source srcData =
ImageDecoder.createSource(wallpaper.getWallpaperFile());
- final int sampleSize = scale;
+ final int finalScale = scale;
+ final int rescaledBitmapWidth = (int) (0.5f + bitmapSize.x / sampleSize);
+ final int rescaledBitmapHeight = (int) (0.5f + bitmapSize.y / sampleSize);
Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> {
- decoder.setTargetSampleSize(sampleSize);
+ if (!multiCrop()) decoder.setTargetSampleSize(finalScale);
+ if (multiCrop()) {
+ decoder.setTargetSize(rescaledBitmapWidth, rescaledBitmapHeight);
+ }
decoder.setCrop(estimateCrop);
});
record.delete();
- if (cropped == null) {
+ if (!multiCrop() && cropped == null) {
Slog.e(TAG, "Could not decode new wallpaper");
} else {
// We are safe to create final crop with safe dimensions now.
- final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
- safeWidth, safeHeight, true);
+ final Bitmap finalCrop = multiCrop() ? cropped
+ : Bitmap.createScaledBitmap(cropped, safeWidth, safeHeight, true);
if (multiCrop()) {
- wallpaper.mSampleSize =
- ((float) cropHint.height()) / finalCrop.getHeight();
+ wallpaper.mSampleSize = sampleSize;
}
if (DEBUG) {
@@ -718,9 +773,7 @@
success = true;
}
} catch (Exception e) {
- if (DEBUG) {
- Slog.e(TAG, "Error decoding crop", e);
- }
+ Slog.e(TAG, "Error decoding crop", e);
} finally {
IoUtils.closeQuietly(bos);
IoUtils.closeQuietly(f);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
index 9e1b5d2..3636f5a 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
@@ -59,6 +59,8 @@
}
private static final String TAG = WallpaperDisplayHelper.class.getSimpleName();
+ private static final float LARGE_SCREEN_MIN_DP = 600f;
+
private final SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
private final DisplayManager mDisplayManager;
private final WindowManagerInternal mWindowManagerInternal;
@@ -67,7 +69,8 @@
// related orientations pairs for foldable (folded orientation, unfolded orientation)
private final List<Pair<Integer, Integer>> mFoldableOrientationPairs = new ArrayList<>();
- private boolean mIsFoldable;
+ private final boolean mIsFoldable;
+ private boolean mIsLargeScreen = false;
WallpaperDisplayHelper(
DisplayManager displayManager,
@@ -94,6 +97,9 @@
mDefaultDisplaySizes.put(orientation, point);
}
}
+
+ mIsLargeScreen |= (displaySize.x / metric.getDensity() >= LARGE_SCREEN_MIN_DP);
+
if (populateOrientationPairs) {
int orientation = WallpaperManager.getOrientation(displaySize);
float newSurface = displaySize.x * displaySize.y
@@ -215,6 +221,13 @@
}
/**
+ * Return true if any of the screens of the default display is considered large (DP >= 600)
+ */
+ boolean isLargeScreen() {
+ return mIsLargeScreen;
+ }
+
+ /**
* If a given orientation corresponds to an unfolded orientation on foldable, return the
* corresponding folded orientation. Otherwise, return UNKNOWN. Always return UNKNOWN if the
* device is not a foldable.
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 9a5961a..f1ba755 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2269,6 +2269,12 @@
Point croppedBitmapSize = new Point(
(int) (0.5f + wallpaper.cropHint.width() / wallpaper.mSampleSize),
(int) (0.5f + wallpaper.cropHint.height() / wallpaper.mSampleSize));
+ if (croppedBitmapSize.equals(0, 0)) {
+ // There is an ImageWallpaper, but there are no crop hints and the bitmap size is
+ // unknown (e.g. the default wallpaper). Return a special "null" value that will be
+ // handled by WallpaperManager, which will fetch the dimensions of the wallpaper.
+ return null;
+ }
SparseArray<Rect> relativeDefaultCrops =
mWallpaperCropper.getDefaultCrops(relativeSuggestedCrops, croppedBitmapSize);
SparseArray<Rect> adjustedRelativeSuggestedCrops = new SparseArray<>();
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
index eb170b7..36e5200 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
@@ -17,6 +17,9 @@
package com.android.server.wearable;
import static android.service.wearable.WearableSensingService.HOTWORD_AUDIO_STREAM_BUNDLE_KEY;
+import static android.system.OsConstants.F_GETFL;
+import static android.system.OsConstants.O_ACCMODE;
+import static android.system.OsConstants.O_RDONLY;
import android.Manifest;
import android.annotation.NonNull;
@@ -42,6 +45,8 @@
import android.service.voice.HotwordAudioStream;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionManagerInternal.WearableHotwordDetectionCallback;
+import android.system.ErrnoException;
+import android.system.Os;
import android.system.OsConstants;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -276,7 +281,10 @@
ParcelFileDescriptor parcelFileDescriptor,
@Nullable IWearableSensingCallback wearableSensingCallback,
RemoteCallback statusCallback) {
- Slog.i(TAG, "onProvideDataStream in per user service.");
+ Slog.i(
+ TAG,
+ "onProvideDataStream in per user service. Is data stream read-only? "
+ + isReadOnly(parcelFileDescriptor));
synchronized (mLock) {
if (!setUpServiceIfNeeded()) {
Slog.w(TAG, "Detection service is not available at this moment.");
@@ -505,10 +513,53 @@
String filename,
AndroidFuture<ParcelFileDescriptor> futureFromWearableSensingService)
throws RemoteException {
- // TODO(b/331395522): Intercept the PFD received from the app process and verify it
- // is read-only
- callbackFromAppProcess.openFile(filename, futureFromWearableSensingService);
+ AndroidFuture<ParcelFileDescriptor> futureFromSystemServer =
+ new AndroidFuture<ParcelFileDescriptor>()
+ .whenComplete(
+ (pfdFromApp, throwable) -> {
+ if (throwable != null) {
+ Slog.e(
+ TAG,
+ "Error when reading file " + filename,
+ throwable);
+ futureFromWearableSensingService.complete(null);
+ return;
+ }
+ if (pfdFromApp == null) {
+ futureFromWearableSensingService.complete(null);
+ return;
+ }
+ if (isReadOnly(pfdFromApp)) {
+ futureFromWearableSensingService.complete(
+ pfdFromApp);
+ } else {
+ Slog.w(
+ TAG,
+ "Received writable ParcelFileDescriptor"
+ + " from app process. To prevent"
+ + " arbitrary data egress, sending null"
+ + " to WearableSensingService"
+ + " instead.");
+ futureFromWearableSensingService.complete(null);
+ }
+ });
+ callbackFromAppProcess.openFile(filename, futureFromSystemServer);
}
};
}
+
+ private static boolean isReadOnly(ParcelFileDescriptor parcelFileDescriptor) {
+ try {
+ int readMode =
+ Os.fcntlInt(parcelFileDescriptor.getFileDescriptor(), F_GETFL, 0) & O_ACCMODE;
+ return readMode == O_RDONLY;
+ } catch (ErrnoException ex) {
+ Slog.w(
+ TAG,
+ "Error encountered when trying to determine if the parcelFileDescriptor is"
+ + " read-only. Treating it as not read-only",
+ ex);
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 5e34596..a821f545 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -35,6 +35,7 @@
import android.provider.Settings;
import android.util.AndroidRuntimeException;
import android.util.Log;
+import android.util.Slog;
import android.webkit.UserPackage;
import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
@@ -201,6 +202,7 @@
ActivityManager.getService().killPackageDependents(packageName,
UserHandle.USER_ALL);
} catch (RemoteException e) {
+ Slog.wtf(TAG, "failed to call killPackageDependents for " + packageName, e);
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 532ff98..dcf20f9 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -186,9 +186,12 @@
}
onWebViewProviderChanged(mCurrentWebViewPackage);
}
+ } catch (WebViewPackageMissingException e) {
+ Slog.e(TAG, "Could not find valid WebView package to create relro with", e);
} catch (Throwable t) {
- // Log and discard errors at this stage as we must not crash the system server.
- Slog.e(TAG, "error preparing webview provider from system server", t);
+ // We don't know a case when this should happen but we log and discard errors at this
+ // stage as we must not crash the system server.
+ Slog.wtf(TAG, "error preparing webview provider from system server", t);
}
if (getCurrentWebViewPackage() == null) {
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index fb338ba..993597e 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -247,9 +247,12 @@
attemptRepair();
}
+ } catch (WebViewPackageMissingException e) {
+ Slog.e(TAG, "Could not find valid WebView package to create relro with", e);
} catch (Throwable t) {
- // Log and discard errors at this stage as we must not crash the system server.
- Slog.e(TAG, "error preparing webview provider from system server", t);
+ // We don't know a case when this should happen but we log and discard errors at this
+ // stage as we must not crash the system server.
+ Slog.wtf(TAG, "error preparing webview provider from system server", t);
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 2b43326..e280bdc 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -466,20 +466,17 @@
}
}
- void drawMagnifiedRegionBorderIfNeeded(int displayId) {
- if (Flags.alwaysDrawMagnificationFullscreenBorder()) {
- return;
- }
-
+ void recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded(int displayId) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(
- TAG + ".drawMagnifiedRegionBorderIfNeeded",
+ TAG + ".recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded",
FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId);
}
+
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.drawMagnifiedRegionBorderIfNeeded();
+ displayMagnifier.recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded();
}
// Not relevant for the window observer.
}
@@ -936,11 +933,13 @@
}
}
- void drawMagnifiedRegionBorderIfNeeded() {
+ void recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded() {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
- mAccessibilityTracing.logTrace(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
+ mAccessibilityTracing.logTrace(LOG_TAG
+ + ".recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded",
FLAGS_MAGNIFICATION_CALLBACK);
}
+ recomputeBounds();
if (!Flags.alwaysDrawMagnificationFullscreenBorder()) {
mMagnifiedViewport.drawWindowIfNeeded();
@@ -1245,7 +1244,6 @@
}
void drawWindowIfNeeded() {
- recomputeBounds();
mWindow.postDrawIfNeeded();
}
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index 9647a62..521f64f 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -389,7 +389,11 @@
return mCheckedOptions;
}
- /** Returns the {@link Runnable} object to clear options Animation. */
+ /**
+ * Returns the {@link Runnable} object to clear options Animation. Note that the runnable
+ * should not be executed inside a lock because the implementation of runnable holds window
+ * manager's lock.
+ */
@Nullable
public Runnable getClearOptionsAnimationRunnable() {
return mClearOptionsAnimation;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 207d42b6..06003e4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -658,6 +658,8 @@
*/
private CompatDisplayInsets mCompatDisplayInsets;
+ private final TaskFragment.ConfigOverrideHint mResolveConfigHint;
+
private static ConstrainDisplayApisConfig sConstrainDisplayApisConfig;
boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session
@@ -1312,6 +1314,7 @@
pw.println(prefix + "supportsPictureInPicture=" + info.supportsPictureInPicture());
pw.println(prefix + "supportsEnterPipOnTaskSwitch: "
+ supportsEnterPipOnTaskSwitch);
+ pw.println(prefix + "mPauseSchedulePendingForPip=" + mPauseSchedulePendingForPip);
}
if (getMaxAspectRatio() != 0) {
pw.println(prefix + "maxAspectRatio=" + getMaxAspectRatio());
@@ -2109,6 +2112,10 @@
mLetterboxUiController = new LetterboxUiController(mWmService, this);
mCameraCompatControlEnabled = mWmService.mContext.getResources()
.getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
+ mResolveConfigHint = new TaskFragment.ConfigOverrideHint();
+ mResolveConfigHint.mUseLegacyInsetsForStableBounds =
+ mWmService.mFlags.mInsetsDecoupledConfiguration
+ && !info.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED);
mTargetSdk = info.applicationInfo.targetSdkVersion;
@@ -4259,7 +4266,8 @@
PendingIntentRecord rec = apr.get();
if (rec != null) {
mAtmService.mPendingIntentController.cancelIntentSender(rec,
- false /* cleanActivity */);
+ false /* cleanActivity */,
+ PendingIntentRecord.CANCEL_REASON_HOSTING_ACTIVITY_DESTROYED);
}
}
pendingResults = null;
@@ -4477,6 +4485,7 @@
getDisplayContent().mOpeningApps.remove(this);
getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
mWmService.mSnapshotController.onAppRemoved(this);
+ mAtmService.mStartingProcessActivities.remove(this);
mTaskSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
mTaskSupervisor.mStoppingActivities.remove(this);
@@ -6219,6 +6228,10 @@
}
boolean shouldBeVisible() {
+ return shouldBeVisible(false /* ignoringKeyguard */);
+ }
+
+ boolean shouldBeVisible(boolean ignoringKeyguard) {
final Task task = getTask();
if (task == null) {
return false;
@@ -6226,7 +6239,7 @@
final boolean behindOccludedContainer = !task.shouldBeVisible(null /* starting */)
|| task.getOccludingActivityAbove(this) != null;
- return shouldBeVisible(behindOccludedContainer, false /* ignoringKeyguard */);
+ return shouldBeVisible(behindOccludedContainer, ignoringKeyguard);
}
void makeVisibleIfNeeded(ActivityRecord starting, boolean reportToClient) {
@@ -8432,7 +8445,8 @@
// The role of CompatDisplayInsets is like the override bounds.
mCompatDisplayInsets =
new CompatDisplayInsets(
- mDisplayContent, this, mLetterboxBoundsForFixedOrientationAndAspectRatio);
+ mDisplayContent, this, mLetterboxBoundsForFixedOrientationAndAspectRatio,
+ mResolveConfigHint.mUseLegacyInsetsForStableBounds);
}
private void clearSizeCompatModeAttributes() {
@@ -8539,12 +8553,13 @@
// If the activity has requested override bounds, the configuration needs to be
// computed accordingly.
if (!matchParentBounds()) {
- getTaskFragment().computeConfigResourceOverrides(resolvedConfig,
- newParentConfiguration);
+ computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
}
// If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
// are already calculated in resolveFixedOrientationConfiguration.
- } else if (!isLetterboxedForFixedOrientationAndAspectRatio()) {
+ // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
+ } else if (!isLetterboxedForFixedOrientationAndAspectRatio()
+ && !mLetterboxUiController.hasFullscreenOverride()) {
resolveAspectRatioRestriction(newParentConfiguration);
}
@@ -8627,16 +8642,15 @@
if (mDisplayContent == null) {
return;
}
- final Rect fullBounds = newParentConfiguration.windowConfiguration.getAppBounds();
+ final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
int rotation = newParentConfiguration.windowConfiguration.getRotation();
if (rotation == ROTATION_UNDEFINED && !isFixedRotationTransforming()) {
rotation = mDisplayContent.getRotation();
}
- if (!mWmService.mFlags.mInsetsDecoupledConfiguration
- || info.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED)
+ if (!mResolveConfigHint.mUseLegacyInsetsForStableBounds
|| getCompatDisplayInsets() != null
- || isFloating(parentWindowingMode) || fullBounds == null
- || fullBounds.isEmpty() || rotation == ROTATION_UNDEFINED) {
+ || isFloating(parentWindowingMode) || parentAppBounds == null
+ || parentAppBounds.isEmpty() || rotation == ROTATION_UNDEFINED) {
// If the insets configuration decoupled logic is not enabled for the app, or the app
// already has a compat override, or the context doesn't contain enough info to
// calculate the override, skip the override.
@@ -8644,15 +8658,20 @@
}
// Override starts here.
- final Rect stableInsets = mDisplayContent.getDisplayPolicy().getDecorInsetsInfo(
- rotation, fullBounds.width(), fullBounds.height()).mOverrideConfigInsets;
+ final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+ final int dw = rotated ? mDisplayContent.mBaseDisplayHeight
+ : mDisplayContent.mBaseDisplayWidth;
+ final int dh = rotated ? mDisplayContent.mBaseDisplayWidth
+ : mDisplayContent.mBaseDisplayHeight;
+ final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy()
+ .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets;
// This should be the only place override the configuration for ActivityRecord. Override
// the value if not calculated yet.
Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
if (outAppBounds == null || outAppBounds.isEmpty()) {
- inOutConfig.windowConfiguration.setAppBounds(fullBounds);
+ inOutConfig.windowConfiguration.setAppBounds(parentAppBounds);
outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
- outAppBounds.inset(stableInsets);
+ outAppBounds.inset(nonDecorInsets);
}
float density = inOutConfig.densityDpi;
if (density == Configuration.DENSITY_DPI_UNDEFINED) {
@@ -8676,11 +8695,10 @@
// For the case of PIP transition and multi-window environment, the
// smallestScreenWidthDp is handled already. Override only if the app is in
// fullscreen.
- final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
DisplayInfo info = new DisplayInfo();
mDisplayContent.getDisplay().getDisplayInfo(info);
- mDisplayContent.computeSizeRanges(info, rotated, info.logicalWidth,
- info.logicalHeight, mDisplayContent.getDisplayMetrics().density,
+ mDisplayContent.computeSizeRanges(info, rotated, dw, dh,
+ mDisplayContent.getDisplayMetrics().density,
inOutConfig, true /* overrideConfig */);
}
@@ -8693,14 +8711,12 @@
}
}
- /**
- * @return The orientation to use to understand if reachability is enabled.
- */
- @Configuration.Orientation
- int getOrientationForReachability() {
- return mLetterboxUiController.hasInheritedLetterboxBehavior()
- ? mLetterboxUiController.getInheritedOrientation()
- : getRequestedConfigurationOrientation();
+ private void computeConfigByResolveHint(@NonNull Configuration resolvedConfig,
+ @NonNull Configuration parentConfig) {
+ task.computeConfigResourceOverrides(resolvedConfig, parentConfig, mResolveConfigHint);
+ // Reset the temp info which should only take effect for the specified computation.
+ mResolveConfigHint.mTmpCompatInsets = null;
+ mResolveConfigHint.mTmpOverrideDisplayInfo = null;
}
/**
@@ -8843,7 +8859,7 @@
}
// Since bounds has changed, the configuration needs to be computed accordingly.
- getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
+ computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
// The position of configuration bounds were calculated in screen space because that is
// easier to resolve the relative position in parent container. However, if the activity is
@@ -8937,17 +8953,18 @@
* to compute the stable bounds.
* @param outStableBounds will store the stable bounds, which are the bounds with insets
* applied, if orientation is not respected when insets are applied.
- * Otherwise outStableBounds will be empty. Stable bounds should be used
- * to compute letterboxed bounds if orientation is not respected when
- * insets are applied.
+ * Stable bounds should be used to compute letterboxed bounds if
+ * orientation is not respected when insets are applied.
+ * @param outNonDecorBounds will store the non decor bounds, which are the bounds with non
+ * decor insets applied, like display cutout and nav bar.
*/
- private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outStableBounds) {
+ private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outStableBounds,
+ Rect outNonDecorBounds) {
outStableBounds.setEmpty();
if (mDisplayContent == null) {
return true;
}
- if (mWmService.mFlags.mInsetsDecoupledConfiguration
- && info.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED)) {
+ if (!mResolveConfigHint.mUseLegacyInsetsForStableBounds) {
// No insets should be considered any more.
return true;
}
@@ -8964,9 +8981,9 @@
? getFixedRotationTransformDisplayInfo()
: mDisplayContent.getDisplayInfo();
final Task task = getTask();
- task.calculateInsetFrames(mTmpBounds /* outNonDecorBounds */,
+ task.calculateInsetFrames(outNonDecorBounds /* outNonDecorBounds */,
outStableBounds /* outStableBounds */, parentBounds /* bounds */, di,
- true /* useLegacyInsetsForStableBounds */);
+ mResolveConfigHint.mUseLegacyInsetsForStableBounds);
final int orientationWithInsets = outStableBounds.height() >= outStableBounds.width()
? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
// If orientation does not match the orientation with insets applied, then a
@@ -8976,9 +8993,6 @@
// have the desired orientation.
final boolean orientationRespectedWithInsets = orientation == orientationWithInsets
|| orientationWithInsets == requestedOrientation;
- if (orientationRespectedWithInsets) {
- outStableBounds.setEmpty();
- }
return orientationRespectedWithInsets;
}
@@ -9004,9 +9018,10 @@
private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) {
final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
final Rect stableBounds = new Rect();
+ final Rect outNonDecorBounds = mTmpBounds;
// If orientation is respected when insets are applied, then stableBounds will be empty.
boolean orientationRespectedWithInsets =
- orientationRespectedWithInsets(parentBounds, stableBounds);
+ orientationRespectedWithInsets(parentBounds, stableBounds, outNonDecorBounds);
if (orientationRespectedWithInsets && handlesOrientationChangeFromDescendant(
getOverrideOrientation())) {
// No need to letterbox because of fixed orientation. Display will handle
@@ -9023,7 +9038,10 @@
final Rect resolvedBounds =
getResolvedOverrideConfiguration().windowConfiguration.getBounds();
- final int parentOrientation = newParentConfig.orientation;
+ final int stableBoundsOrientation = stableBounds.width() > stableBounds.height()
+ ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+ final int parentOrientation = mResolveConfigHint.mUseLegacyInsetsForStableBounds
+ ? stableBoundsOrientation : newParentConfig.orientation;
// If the activity requires a different orientation (either by override or activityInfo),
// make it fit the available bounds by scaling down its bounds.
@@ -9046,10 +9064,12 @@
return;
}
+ final Rect parentAppBounds = mResolveConfigHint.mUseLegacyInsetsForStableBounds
+ ? outNonDecorBounds : newParentConfig.windowConfiguration.getAppBounds();
// TODO(b/182268157): Explore using only one type of parentBoundsWithInsets, either app
// bounds or stable bounds to unify aspect ratio logic.
final Rect parentBoundsWithInsets = orientationRespectedWithInsets
- ? newParentConfig.windowConfiguration.getAppBounds() : stableBounds;
+ ? parentAppBounds : stableBounds;
final Rect containingBounds = new Rect();
final Rect containingBoundsWithInsets = new Rect();
// Need to shrink the containing bounds into a square because the parent orientation
@@ -9060,8 +9080,8 @@
// vertically centered within parent bounds with insets, so position vertical bounds
// within parent bounds with insets to prevent insets from unnecessarily trimming
// vertical bounds.
- final int bottom = Math.min(parentBoundsWithInsets.top + parentBounds.width() - 1,
- parentBoundsWithInsets.bottom);
+ final int bottom = Math.min(parentBoundsWithInsets.top
+ + parentBoundsWithInsets.width() - 1, parentBoundsWithInsets.bottom);
containingBounds.set(parentBounds.left, parentBoundsWithInsets.top, parentBounds.right,
bottom);
containingBoundsWithInsets.set(parentBoundsWithInsets.left, parentBoundsWithInsets.top,
@@ -9072,8 +9092,8 @@
// horizontally centered within parent bounds with insets, so position horizontal bounds
// within parent bounds with insets to prevent insets from unnecessarily trimming
// horizontal bounds.
- final int right = Math.min(parentBoundsWithInsets.left + parentBounds.height(),
- parentBoundsWithInsets.right);
+ final int right = Math.min(parentBoundsWithInsets.left
+ + parentBoundsWithInsets.height(), parentBoundsWithInsets.right);
containingBounds.set(parentBoundsWithInsets.left, parentBounds.top, right,
parentBounds.bottom);
containingBoundsWithInsets.set(parentBoundsWithInsets.left, parentBoundsWithInsets.top,
@@ -9126,8 +9146,8 @@
// Calculate app bounds using fixed orientation bounds because they will be needed later
// for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
- getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
- newParentConfig, compatDisplayInsets);
+ mResolveConfigHint.mTmpCompatInsets = compatDisplayInsets;
+ computeConfigByResolveHint(getResolvedOverrideConfiguration(), newParentConfig);
mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds);
}
@@ -9168,8 +9188,8 @@
if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
// Compute the configuration based on the resolved bounds. If aspect ratio doesn't
// restrict, the bounds should be the requested override bounds.
- getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
- getFixedRotationTransformDisplayInfo());
+ mResolveConfigHint.mTmpOverrideDisplayInfo = getFixedRotationTransformDisplayInfo();
+ computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
}
}
@@ -9233,8 +9253,8 @@
// Use resolvedBounds to compute other override configurations such as appBounds. The bounds
// are calculated in compat container space. The actual position on screen will be applied
// later, so the calculation is simpler that doesn't need to involve offset from parent.
- getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
- compatDisplayInsets);
+ mResolveConfigHint.mTmpCompatInsets = compatDisplayInsets;
+ computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
// Use current screen layout as source because the size of app is independent to parent.
resolvedConfig.screenLayout = computeScreenLayout(
getConfiguration().screenLayout, resolvedConfig.screenWidthDp,
@@ -10507,8 +10527,7 @@
if (!getTurnScreenOnFlag()) {
return false;
}
- final Task rootTask = getRootTask();
- return mCurrentLaunchCanTurnScreenOn && rootTask != null
+ return mCurrentLaunchCanTurnScreenOn
&& mTaskSupervisor.getKeyguardController().checkKeyguardVisibility(this);
}
@@ -10752,7 +10771,7 @@
/** Constructs the environment to simulate the bounds behavior of the given container. */
CompatDisplayInsets(DisplayContent display, ActivityRecord container,
- @Nullable Rect fixedOrientationBounds) {
+ @Nullable Rect fixedOrientationBounds, boolean useOverrideInsets) {
mOriginalRotation = display.getRotation();
mIsFloating = container.getWindowConfiguration().tasksAreFloating();
mOriginalRequestedOrientation = container.getRequestedConfigurationOrientation();
@@ -10804,8 +10823,13 @@
final int dh = rotated ? display.mBaseDisplayWidth : display.mBaseDisplayHeight;
final DisplayPolicy.DecorInsets.Info decorInfo =
policy.getDecorInsetsInfo(rotation, dw, dh);
- mNonDecorInsets[rotation].set(decorInfo.mNonDecorInsets);
- mStableInsets[rotation].set(decorInfo.mConfigInsets);
+ if (useOverrideInsets) {
+ mStableInsets[rotation].set(decorInfo.mOverrideConfigInsets);
+ mNonDecorInsets[rotation].set(decorInfo.mOverrideNonDecorInsets);
+ } else {
+ mStableInsets[rotation].set(decorInfo.mConfigInsets);
+ mNonDecorInsets[rotation].set(decorInfo.mNonDecorInsets);
+ }
if (unfilledContainerBounds == null) {
continue;
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 182e1c1..2cccd33 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -523,8 +523,11 @@
void onActivityLaunched(TaskInfo taskInfo, ActivityRecord r) {
final SparseArray<ActivityInterceptorCallback> callbacks =
mService.getActivityInterceptorCallbacks();
- ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo(
- r::clearOptionsAnimationForSiblings);
+ final ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo(() -> {
+ synchronized (mService.mGlobalLock) {
+ r.clearOptionsAnimationForSiblings();
+ }
+ });
for (int i = 0; i < callbacks.size(); i++) {
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
callback.onActivityLaunched(taskInfo, r.info, info);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 354cab3..a739e57 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -390,6 +390,9 @@
final VisibleActivityProcessTracker mVisibleActivityProcessTracker;
+ /** The starting activities which are waiting for their processes to attach. */
+ final ArrayList<ActivityRecord> mStartingProcessActivities = new ArrayList<>();
+
/* Global service lock used by the package the owns this service. */
final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
/**
@@ -883,6 +886,7 @@
mRecentTasks.onSystemReadyLocked();
mTaskSupervisor.onSystemReady();
mActivityClientController.onSystemReady();
+ mAppWarnings.onSystemReady();
// TODO(b/258792202) Cleanup once ASM is ready to launch
ActivitySecurityModelFeatureFlags.initialize(mContext.getMainExecutor());
mGrammaticalManagerInternal = LocalServices.getService(
@@ -3732,16 +3736,17 @@
return false;
}
- // If PiP2 flag is on and client-request to enter PiP comes in,
- // we request a direct transition from Shell to TRANSIT_PIP to get the startWct
- // with the right entry bounds. So PiP activity isn't moved to a pinned task until after
- // Shell calls back into Core with the entry bounds passed through.
if (isPip2ExperimentEnabled()) {
- final Transition legacyEnterPipTransition = new Transition(TRANSIT_PIP,
+ // If PiP2 flag is on and request to enter PiP comes in,
+ // we request a direct transition TRANSIT_PIP from Shell to get the right entry bounds.
+ // So PiP activity isn't moved to a pinned task until after
+ // Shell calls back into Core with the entry bounds to be applied with startWCT.
+ final Transition enterPipTransition = new Transition(TRANSIT_PIP,
0 /* flags */, getTransitionController(), mWindowManager.mSyncEngine);
- legacyEnterPipTransition.setPipActivity(r);
- getTransitionController().startCollectOrQueue(legacyEnterPipTransition, (deferred) -> {
- getTransitionController().requestStartTransition(legacyEnterPipTransition,
+ enterPipTransition.setPipActivity(r);
+ r.mAutoEnteringPip = isAutoEnter;
+ getTransitionController().startCollectOrQueue(enterPipTransition, (deferred) -> {
+ getTransitionController().requestStartTransition(enterPipTransition,
r.getTask(), null /* remoteTransition */, null /* displayChange */);
});
return true;
@@ -3786,7 +3791,8 @@
// Continue the pausing process after entering pip.
if (r.isState(PAUSING) && r.mPauseSchedulePendingForPip) {
r.getTask().schedulePauseActivity(r, false /* userLeaving */,
- false /* pauseImmediately */, true /* autoEnteringPip */, "auto-pip");
+ false /* pauseImmediately */, true /* autoEnteringPip */,
+ "auto-pip");
}
r.mAutoEnteringPip = false;
}
@@ -4322,6 +4328,9 @@
if (mDemoteTopAppReasons != 0) {
pw.println(" mDemoteTopAppReasons=" + mDemoteTopAppReasons);
}
+ if (!mStartingProcessActivities.isEmpty()) {
+ pw.println(" mStartingProcessActivities=" + mStartingProcessActivities);
+ }
}
if (!printedAnything) {
@@ -5169,6 +5178,13 @@
void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
String hostingType) {
+ if (!mStartingProcessActivities.contains(activity)) {
+ mStartingProcessActivities.add(activity);
+ } else if (mProcessNames.get(
+ activity.processName, activity.info.applicationInfo.uid) != null) {
+ // The process is already starting. Wait for it to attach.
+ return;
+ }
try {
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"
@@ -6165,7 +6181,20 @@
@Override
public void onProcessRemoved(String name, int uid) {
synchronized (mGlobalLockWithoutBoost) {
- mProcessNames.remove(name, uid);
+ final WindowProcessController proc = mProcessNames.remove(name, uid);
+ if (proc != null && !mStartingProcessActivities.isEmpty()) {
+ for (int i = mStartingProcessActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = mStartingProcessActivities.get(i);
+ if (uid == r.info.applicationInfo.uid && name.equals(r.processName)) {
+ Slog.w(TAG, proc + " is removed with pending start " + r);
+ mStartingProcessActivities.remove(i);
+ // If visible, finish it to avoid getting stuck on screen.
+ if (r.isVisibleRequested()) {
+ r.finishIfPossible("starting-proc-removed", false /* oomAdj */);
+ }
+ }
+ }
+ }
}
}
@@ -6325,7 +6354,7 @@
public void onPackageDataCleared(String name, int userId) {
synchronized (mGlobalLock) {
mCompatModePackages.handlePackageDataClearedLocked(name);
- mAppWarnings.onPackageDataCleared(name);
+ mAppWarnings.onPackageDataCleared(name, userId);
mPackageConfigPersister.onPackageDataCleared(name, userId);
}
}
@@ -6333,7 +6362,7 @@
@Override
public void onPackageUninstalled(String name, int userId) {
synchronized (mGlobalLock) {
- mAppWarnings.onPackageUninstalled(name);
+ mAppWarnings.onPackageUninstalled(name, userId);
mCompatModePackages.handlePackageUninstalledLocked(name);
mPackageConfigPersister.onPackageUninstall(name, userId);
}
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index ad5f442..9fd543f 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -16,8 +16,14 @@
package com.android.server.wm;
+import static android.os.UserHandle.USER_NULL;
+import static android.os.UserHandle.USER_SYSTEM;
+import static android.os.UserManager.isHeadlessSystemUserMode;
+import static android.os.UserManager.isVisibleBackgroundUsersEnabled;
+
import android.annotation.NonNull;
import android.annotation.UiThread;
+import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
@@ -26,17 +32,21 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.DisplayMetrics;
+import android.util.Pair;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -44,6 +54,8 @@
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.IoThread;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -65,19 +77,30 @@
public static final int FLAG_HIDE_DEPRECATED_SDK = 0x04;
public static final int FLAG_HIDE_DEPRECATED_ABI = 0x08;
+ /**
+ * Map of package flags for each user.
+ * Key: {@literal Pair<userId, packageName>}
+ * Value: Flags
+ */
@GuardedBy("mPackageFlags")
- private final ArrayMap<String, Integer> mPackageFlags = new ArrayMap<>();
+ private final ArrayMap<Pair<Integer, String>, Integer> mPackageFlags = new ArrayMap<>();
private final ActivityTaskManagerService mAtm;
- private final Context mUiContext;
private final WriteConfigTask mWriteConfigTask;
private final UiHandler mUiHandler;
private final AtomicFile mConfigFile;
- private UnsupportedDisplaySizeDialog mUnsupportedDisplaySizeDialog;
- private UnsupportedCompileSdkDialog mUnsupportedCompileSdkDialog;
- private DeprecatedTargetSdkVersionDialog mDeprecatedTargetSdkVersionDialog;
- private DeprecatedAbiDialog mDeprecatedAbiDialog;
+ private UserManagerInternal mUserManagerInternal;
+
+ /**
+ * Maps of app warning dialogs for each user.
+ * Key: userId
+ * Value: The warning dialog for specific user
+ */
+ private SparseArray<UnsupportedDisplaySizeDialog> mUnsupportedDisplaySizeDialogs;
+ private SparseArray<UnsupportedCompileSdkDialog> mUnsupportedCompileSdkDialogs;
+ private SparseArray<DeprecatedTargetSdkVersionDialog> mDeprecatedTargetSdkVersionDialogs;
+ private SparseArray<DeprecatedAbiDialog> mDeprecatedAbiDialogs;
/** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
private final ArraySet<ComponentName> mAlwaysShowUnsupportedCompileSdkWarningActivities =
@@ -92,12 +115,35 @@
public AppWarnings(ActivityTaskManagerService atm, Context uiContext, Handler handler,
Handler uiHandler, File systemDir) {
mAtm = atm;
- mUiContext = uiContext;
mWriteConfigTask = new WriteConfigTask();
mUiHandler = new UiHandler(uiHandler.getLooper());
mConfigFile = new AtomicFile(new File(systemDir, CONFIG_FILE_NAME), "warnings-config");
+ }
+ /**
+ * Called when ActivityManagerService receives its systemReady call during boot.
+ */
+ void onSystemReady() {
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
readConfigFromFileAmsThread();
+
+ if (!isVisibleBackgroundUsersEnabled()) {
+ return;
+ }
+
+ mUserManagerInternal.addUserLifecycleListener(
+ new UserManagerInternal.UserLifecycleListener() {
+ @Override
+ public void onUserRemoved(UserInfo user) {
+ // Ignore profile user.
+ if (!user.isFull()) {
+ return;
+ }
+ // Dismiss all warnings and clear all package flags for the user.
+ mUiHandler.hideDialogsForPackage(/* name= */ null, user.id);
+ clearAllPackageFlagsForUser(user.id);
+ }
+ });
}
/**
@@ -227,18 +273,20 @@
* Called by ActivityManagerService when package data has been cleared.
*
* @param name the package whose data has been cleared
+ * @param userId the user where the package resides.
*/
- public void onPackageDataCleared(String name) {
- removePackageAndHideDialogs(name);
+ public void onPackageDataCleared(String name, int userId) {
+ removePackageAndHideDialogs(name, userId);
}
/**
* Called by ActivityManagerService when a package has been uninstalled.
*
* @param name the package that has been uninstalled
+ * @param userId the user where the package resides.
*/
- public void onPackageUninstalled(String name) {
- removePackageAndHideDialogs(name);
+ public void onPackageUninstalled(String name, int userId) {
+ removePackageAndHideDialogs(name, userId);
}
/**
@@ -251,11 +299,24 @@
/**
* Does what it says on the tin.
*/
- private void removePackageAndHideDialogs(String name) {
- mUiHandler.hideDialogsForPackage(name);
+ private void removePackageAndHideDialogs(String name, int userId) {
+ // Per-user AppWarnings only affects the behavior of the devices that enable the visible
+ // background users.
+ // To preserve existing behavior of the other devices, handle AppWarnings as a system user
+ // regardless of the actual user.
+ if (!isVisibleBackgroundUsersEnabled()) {
+ userId = USER_SYSTEM;
+ } else {
+ // If the userId is of a profile, use the parent user ID,
+ // since the warning dialogs and the flags for a package are handled per profile group.
+ userId = mUserManagerInternal.getProfileParentId(userId);
+ }
+
+ mUiHandler.hideDialogsForPackage(name, userId);
synchronized (mPackageFlags) {
- if (mPackageFlags.remove(name) != null) {
+ final Pair<Integer, String> packageKey = Pair.create(userId, name);
+ if (mPackageFlags.remove(packageKey) != null) {
mWriteConfigTask.schedule();
}
}
@@ -268,10 +329,14 @@
*/
@UiThread
private void hideUnsupportedDisplaySizeDialogUiThread() {
- if (mUnsupportedDisplaySizeDialog != null) {
- mUnsupportedDisplaySizeDialog.dismiss();
- mUnsupportedDisplaySizeDialog = null;
+ if (mUnsupportedDisplaySizeDialogs == null) {
+ return;
}
+
+ for (int i = 0; i < mUnsupportedDisplaySizeDialogs.size(); i++) {
+ mUnsupportedDisplaySizeDialogs.valueAt(i).dismiss();
+ }
+ mUnsupportedDisplaySizeDialogs.clear();
}
/**
@@ -282,16 +347,24 @@
* @param ar record for the activity that triggered the warning
*/
@UiThread
- private void showUnsupportedDisplaySizeDialogUiThread(ActivityRecord ar) {
- if (mUnsupportedDisplaySizeDialog != null) {
- mUnsupportedDisplaySizeDialog.dismiss();
- mUnsupportedDisplaySizeDialog = null;
+ private void showUnsupportedDisplaySizeDialogUiThread(@NonNull ActivityRecord ar) {
+ final int userId = getUserIdForActivity(ar);
+ UnsupportedDisplaySizeDialog unsupportedDisplaySizeDialog;
+ if (mUnsupportedDisplaySizeDialogs != null) {
+ unsupportedDisplaySizeDialog = mUnsupportedDisplaySizeDialogs.get(userId);
+ if (unsupportedDisplaySizeDialog != null) {
+ unsupportedDisplaySizeDialog.dismiss();
+ mUnsupportedDisplaySizeDialogs.remove(userId);
+ }
}
- if (ar != null && !hasPackageFlag(
- ar.packageName, FLAG_HIDE_DISPLAY_SIZE)) {
- mUnsupportedDisplaySizeDialog = new UnsupportedDisplaySizeDialog(
- AppWarnings.this, mUiContext, ar.info.applicationInfo);
- mUnsupportedDisplaySizeDialog.show();
+ if (!hasPackageFlag(userId, ar.packageName, FLAG_HIDE_DISPLAY_SIZE)) {
+ unsupportedDisplaySizeDialog = new UnsupportedDisplaySizeDialog(
+ AppWarnings.this, getUiContextForActivity(ar), ar.info.applicationInfo, userId);
+ unsupportedDisplaySizeDialog.show();
+ if (mUnsupportedDisplaySizeDialogs == null) {
+ mUnsupportedDisplaySizeDialogs = new SparseArray<>();
+ }
+ mUnsupportedDisplaySizeDialogs.put(userId, unsupportedDisplaySizeDialog);
}
}
@@ -303,16 +376,24 @@
* @param ar record for the activity that triggered the warning
*/
@UiThread
- private void showUnsupportedCompileSdkDialogUiThread(ActivityRecord ar) {
- if (mUnsupportedCompileSdkDialog != null) {
- mUnsupportedCompileSdkDialog.dismiss();
- mUnsupportedCompileSdkDialog = null;
+ private void showUnsupportedCompileSdkDialogUiThread(@NonNull ActivityRecord ar) {
+ final int userId = getUserIdForActivity(ar);
+ UnsupportedCompileSdkDialog unsupportedCompileSdkDialog;
+ if (mUnsupportedCompileSdkDialogs != null) {
+ unsupportedCompileSdkDialog = mUnsupportedCompileSdkDialogs.get(userId);
+ if (unsupportedCompileSdkDialog != null) {
+ unsupportedCompileSdkDialog.dismiss();
+ mUnsupportedCompileSdkDialogs.remove(userId);
+ }
}
- if (ar != null && !hasPackageFlag(
- ar.packageName, FLAG_HIDE_COMPILE_SDK)) {
- mUnsupportedCompileSdkDialog = new UnsupportedCompileSdkDialog(
- AppWarnings.this, mUiContext, ar.info.applicationInfo);
- mUnsupportedCompileSdkDialog.show();
+ if (!hasPackageFlag(userId, ar.packageName, FLAG_HIDE_COMPILE_SDK)) {
+ unsupportedCompileSdkDialog = new UnsupportedCompileSdkDialog(
+ AppWarnings.this, getUiContextForActivity(ar), ar.info.applicationInfo, userId);
+ unsupportedCompileSdkDialog.show();
+ if (mUnsupportedCompileSdkDialogs == null) {
+ mUnsupportedCompileSdkDialogs = new SparseArray<>();
+ }
+ mUnsupportedCompileSdkDialogs.put(userId, unsupportedCompileSdkDialog);
}
}
@@ -324,16 +405,24 @@
* @param ar record for the activity that triggered the warning
*/
@UiThread
- private void showDeprecatedTargetSdkDialogUiThread(ActivityRecord ar) {
- if (mDeprecatedTargetSdkVersionDialog != null) {
- mDeprecatedTargetSdkVersionDialog.dismiss();
- mDeprecatedTargetSdkVersionDialog = null;
+ private void showDeprecatedTargetSdkDialogUiThread(@NonNull ActivityRecord ar) {
+ final int userId = getUserIdForActivity(ar);
+ DeprecatedTargetSdkVersionDialog deprecatedTargetSdkVersionDialog;
+ if (mDeprecatedTargetSdkVersionDialogs != null) {
+ deprecatedTargetSdkVersionDialog = mDeprecatedTargetSdkVersionDialogs.get(userId);
+ if (deprecatedTargetSdkVersionDialog != null) {
+ deprecatedTargetSdkVersionDialog.dismiss();
+ mDeprecatedTargetSdkVersionDialogs.remove(userId);
+ }
}
- if (ar != null && !hasPackageFlag(
- ar.packageName, FLAG_HIDE_DEPRECATED_SDK)) {
- mDeprecatedTargetSdkVersionDialog = new DeprecatedTargetSdkVersionDialog(
- AppWarnings.this, mUiContext, ar.info.applicationInfo);
- mDeprecatedTargetSdkVersionDialog.show();
+ if (!hasPackageFlag(userId, ar.packageName, FLAG_HIDE_DEPRECATED_SDK)) {
+ deprecatedTargetSdkVersionDialog = new DeprecatedTargetSdkVersionDialog(
+ AppWarnings.this, getUiContextForActivity(ar), ar.info.applicationInfo, userId);
+ deprecatedTargetSdkVersionDialog.show();
+ if (mDeprecatedTargetSdkVersionDialogs == null) {
+ mDeprecatedTargetSdkVersionDialogs = new SparseArray<>();
+ }
+ mDeprecatedTargetSdkVersionDialogs.put(userId, deprecatedTargetSdkVersionDialog);
}
}
@@ -345,16 +434,24 @@
* @param ar record for the activity that triggered the warning
*/
@UiThread
- private void showDeprecatedAbiDialogUiThread(ActivityRecord ar) {
- if (mDeprecatedAbiDialog != null) {
- mDeprecatedAbiDialog.dismiss();
- mDeprecatedAbiDialog = null;
+ private void showDeprecatedAbiDialogUiThread(@NonNull ActivityRecord ar) {
+ final int userId = getUserIdForActivity(ar);
+ DeprecatedAbiDialog deprecatedAbiDialog;
+ if (mDeprecatedAbiDialogs != null) {
+ deprecatedAbiDialog = mDeprecatedAbiDialogs.get(userId);
+ if (deprecatedAbiDialog != null) {
+ deprecatedAbiDialog.dismiss();
+ mDeprecatedAbiDialogs.remove(userId);
+ }
}
- if (ar != null && !hasPackageFlag(
- ar.packageName, FLAG_HIDE_DEPRECATED_ABI)) {
- mDeprecatedAbiDialog = new DeprecatedAbiDialog(
- AppWarnings.this, mUiContext, ar.info.applicationInfo);
- mDeprecatedAbiDialog.show();
+ if (!hasPackageFlag(userId, ar.packageName, FLAG_HIDE_DEPRECATED_ABI)) {
+ deprecatedAbiDialog = new DeprecatedAbiDialog(
+ AppWarnings.this, getUiContextForActivity(ar), ar.info.applicationInfo, userId);
+ deprecatedAbiDialog.show();
+ if (mDeprecatedAbiDialogs == null) {
+ mDeprecatedAbiDialogs = new SparseArray<>();
+ }
+ mDeprecatedAbiDialogs.put(userId, deprecatedAbiDialog);
}
}
@@ -365,65 +462,84 @@
*
* @param name the package for which warnings should be dismissed, or {@code null} to dismiss
* all warnings
+ * @param userId the user where the package resides.
*/
@UiThread
- private void hideDialogsForPackageUiThread(String name) {
+ private void hideDialogsForPackageUiThread(String name, int userId) {
// Hides the "unsupported display" dialog if necessary.
- if (mUnsupportedDisplaySizeDialog != null && (name == null || name.equals(
- mUnsupportedDisplaySizeDialog.mPackageName))) {
- mUnsupportedDisplaySizeDialog.dismiss();
- mUnsupportedDisplaySizeDialog = null;
+ if (mUnsupportedDisplaySizeDialogs != null) {
+ UnsupportedDisplaySizeDialog unsupportedDisplaySizeDialog =
+ mUnsupportedDisplaySizeDialogs.get(userId);
+ if (unsupportedDisplaySizeDialog != null && (name == null || name.equals(
+ unsupportedDisplaySizeDialog.mPackageName))) {
+ unsupportedDisplaySizeDialog.dismiss();
+ mUnsupportedDisplaySizeDialogs.remove(userId);
+ }
}
// Hides the "unsupported compile SDK" dialog if necessary.
- if (mUnsupportedCompileSdkDialog != null && (name == null || name.equals(
- mUnsupportedCompileSdkDialog.mPackageName))) {
- mUnsupportedCompileSdkDialog.dismiss();
- mUnsupportedCompileSdkDialog = null;
+ if (mUnsupportedCompileSdkDialogs != null) {
+ UnsupportedCompileSdkDialog unsupportedCompileSdkDialog =
+ mUnsupportedCompileSdkDialogs.get(userId);
+ if (unsupportedCompileSdkDialog != null && (name == null || name.equals(
+ unsupportedCompileSdkDialog.mPackageName))) {
+ unsupportedCompileSdkDialog.dismiss();
+ mUnsupportedCompileSdkDialogs.remove(userId);
+ }
}
// Hides the "deprecated target sdk version" dialog if necessary.
- if (mDeprecatedTargetSdkVersionDialog != null && (name == null || name.equals(
- mDeprecatedTargetSdkVersionDialog.mPackageName))) {
- mDeprecatedTargetSdkVersionDialog.dismiss();
- mDeprecatedTargetSdkVersionDialog = null;
+ if (mDeprecatedTargetSdkVersionDialogs != null) {
+ DeprecatedTargetSdkVersionDialog deprecatedTargetSdkVersionDialog =
+ mDeprecatedTargetSdkVersionDialogs.get(userId);
+ if (deprecatedTargetSdkVersionDialog != null && (name == null || name.equals(
+ deprecatedTargetSdkVersionDialog.mPackageName))) {
+ deprecatedTargetSdkVersionDialog.dismiss();
+ mDeprecatedTargetSdkVersionDialogs.remove(userId);
+ }
}
// Hides the "deprecated abi" dialog if necessary.
- if (mDeprecatedAbiDialog != null && (name == null || name.equals(
- mDeprecatedAbiDialog.mPackageName))) {
- mDeprecatedAbiDialog.dismiss();
- mDeprecatedAbiDialog = null;
+ if (mDeprecatedAbiDialogs != null) {
+ DeprecatedAbiDialog deprecatedAbiDialog = mDeprecatedAbiDialogs.get(userId);
+ if (deprecatedAbiDialog != null && (name == null || name.equals(
+ deprecatedAbiDialog.mPackageName))) {
+ deprecatedAbiDialog.dismiss();
+ mDeprecatedAbiDialogs.remove(userId);
+ }
}
}
/**
* Returns the value of the flag for the given package.
*
+ * @param userId the user where the package resides.
* @param name the package from which to retrieve the flag
* @param flag the bitmask for the flag to retrieve
* @return {@code true} if the flag is enabled, {@code false} otherwise
*/
- boolean hasPackageFlag(String name, int flag) {
- return (getPackageFlags(name) & flag) == flag;
+ boolean hasPackageFlag(int userId, String name, int flag) {
+ return (getPackageFlags(userId, name) & flag) == flag;
}
/**
* Sets the flag for the given package to the specified value.
*
+ * @param userId the user where the package resides.
* @param name the package on which to set the flag
* @param flag the bitmask for flag to set
* @param enabled the value to set for the flag
*/
- void setPackageFlag(String name, int flag, boolean enabled) {
+ void setPackageFlag(int userId, String name, int flag, boolean enabled) {
synchronized (mPackageFlags) {
- final int curFlags = getPackageFlags(name);
+ final int curFlags = getPackageFlags(userId, name);
final int newFlags = enabled ? (curFlags | flag) : (curFlags & ~flag);
if (curFlags != newFlags) {
+ final Pair<Integer, String> packageKey = Pair.create(userId, name);
if (newFlags != 0) {
- mPackageFlags.put(name, newFlags);
+ mPackageFlags.put(packageKey, newFlags);
} else {
- mPackageFlags.remove(name);
+ mPackageFlags.remove(packageKey);
}
mWriteConfigTask.schedule();
}
@@ -433,13 +549,95 @@
/**
* Returns the bitmask of flags set for the specified package.
*/
- private int getPackageFlags(String name) {
+ private int getPackageFlags(int userId, String packageName) {
synchronized (mPackageFlags) {
- return mPackageFlags.getOrDefault(name, 0);
+ final Pair<Integer, String> packageKey = Pair.create(userId, packageName);
+ return mPackageFlags.getOrDefault(packageKey, 0);
}
}
/**
+ * Clear all the package flags for given user.
+ */
+ private void clearAllPackageFlagsForUser(int userId) {
+ synchronized (mPackageFlags) {
+ boolean hasPackageFlagsForUser = false;
+ for (int i = mPackageFlags.size() - 1; i >= 0; i--) {
+ Pair<Integer, String> key = mPackageFlags.keyAt(i);
+ if (key.first == userId) {
+ hasPackageFlagsForUser = true;
+ mPackageFlags.remove(key);
+ }
+ }
+
+ if (hasPackageFlagsForUser) {
+ mWriteConfigTask.schedule();
+ }
+ }
+ }
+
+ /**
+ * Returns the user ID for handling AppWarnings per user.
+ * Per-user AppWarnings only affects the behavior of the devices that enable
+ * the visible background users.
+ * If the device doesn't enable visible background users, it will return the system user ID
+ * for handling AppWarnings as a system user regardless of the actual user
+ * to preserve existing behavior of the device.
+ * Otherwise, it will return the main user (i.e., not a profile) that is assigned to the display
+ * where the activity is launched.
+ */
+ private @UserIdInt int getUserIdForActivity(@NonNull ActivityRecord ar) {
+ if (!isVisibleBackgroundUsersEnabled()) {
+ return USER_SYSTEM;
+ }
+
+ if (ar.mUserId == USER_SYSTEM) {
+ return getUserAssignedToDisplay(ar.mDisplayContent.getDisplayId());
+ }
+
+ return mUserManagerInternal.getProfileParentId(ar.mUserId);
+ }
+
+ /**
+ * Returns the UI context for handling AppWarnings per user.
+ * Per-user AppWarnings only affects the behavior of the devices that enable
+ * the visible background users.
+ * If the device enables the visible background users, it will return the UI context associated
+ * with the assigned user and the display where the activity is launched.
+ * If the HSUM device doesn't enable the visible background users, it will return the UI context
+ * associated with the current user and the default display.
+ * Otherwise, it will return the UI context associated with the system user and the default
+ * display.
+ */
+ private Context getUiContextForActivity(@NonNull ActivityRecord ar) {
+ if (!isVisibleBackgroundUsersEnabled()) {
+ if (!isHeadlessSystemUserMode()) {
+ return mAtm.getUiContext();
+ }
+
+ Context uiContextForCurrentUser = mAtm.getUiContext().createContextAsUser(
+ new UserHandle(mAtm.getCurrentUserId()), /* flags= */ 0);
+ return uiContextForCurrentUser;
+ }
+
+ DisplayContent dc = ar.mDisplayContent;
+ Context systemUiContext = dc.getDisplayPolicy().getSystemUiContext();
+ int assignedUser = getUserAssignedToDisplay(dc.getDisplayId());
+ Context uiContextForUser = systemUiContext.createContextAsUser(
+ new UserHandle(assignedUser), /* flags= */ 0);
+ return uiContextForUser;
+ }
+
+ /**
+ * Returns the main user that is assigned to the display.
+ *
+ * See {@link UserManagerInternal#getUserAssignedToDisplay(int)}.
+ */
+ private @UserIdInt int getUserAssignedToDisplay(int displayId) {
+ return mUserManagerInternal.getUserAssignedToDisplay(displayId);
+ }
+
+ /**
* Handles messages on the system process UI thread.
*/
private final class UiHandler extends Handler {
@@ -470,7 +668,8 @@
} break;
case MSG_HIDE_DIALOGS_FOR_PACKAGE: {
final String name = (String) msg.obj;
- hideDialogsForPackageUiThread(name);
+ final int userId = (int) msg.arg1;
+ hideDialogsForPackageUiThread(name, userId);
} break;
case MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG: {
final ActivityRecord ar = (ActivityRecord) msg.obj;
@@ -508,20 +707,24 @@
obtainMessage(MSG_SHOW_DEPRECATED_ABI_DIALOG, r).sendToTarget();
}
- public void hideDialogsForPackage(String name) {
- obtainMessage(MSG_HIDE_DIALOGS_FOR_PACKAGE, name).sendToTarget();
+ public void hideDialogsForPackage(String name, int userId) {
+ obtainMessage(MSG_HIDE_DIALOGS_FOR_PACKAGE, userId, 0, name).sendToTarget();
}
}
static class BaseDialog {
final AppWarnings mManager;
+ final Context mUiContext;
final String mPackageName;
+ final int mUserId;
AlertDialog mDialog;
private BroadcastReceiver mCloseReceiver;
- BaseDialog(AppWarnings manager, String packageName) {
+ BaseDialog(AppWarnings manager, Context uiContext, String packageName, int userId) {
mManager = manager;
+ mUiContext = uiContext;
mPackageName = packageName;
+ mUserId = userId;
}
@UiThread
@@ -532,11 +735,11 @@
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
- mManager.mUiHandler.hideDialogsForPackage(mPackageName);
+ mManager.mUiHandler.hideDialogsForPackage(mPackageName, mUserId);
}
}
};
- mManager.mUiContext.registerReceiver(mCloseReceiver,
+ mUiContext.registerReceiver(mCloseReceiver,
new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
Context.RECEIVER_EXPORTED);
}
@@ -548,7 +751,7 @@
void dismiss() {
if (mDialog == null) return;
if (mCloseReceiver != null) {
- mManager.mUiContext.unregisterReceiver(mCloseReceiver);
+ mUiContext.unregisterReceiver(mCloseReceiver);
mCloseReceiver = null;
}
mDialog.dismiss();
@@ -558,12 +761,13 @@
private final class WriteConfigTask implements Runnable {
private static final long WRITE_CONFIG_DELAY_MS = 10000;
- final AtomicReference<ArrayMap<String, Integer>> mPendingPackageFlags =
+ final AtomicReference<ArrayMap<Pair<Integer, String>, Integer>> mPendingPackageFlags =
new AtomicReference<>();
@Override
public void run() {
- final ArrayMap<String, Integer> packageFlags = mPendingPackageFlags.getAndSet(null);
+ final ArrayMap<Pair<Integer, String>, Integer> packageFlags =
+ mPendingPackageFlags.getAndSet(null);
if (packageFlags != null) {
writeConfigToFile(packageFlags);
}
@@ -579,7 +783,7 @@
/** Writes the configuration file. */
@WorkerThread
- private void writeConfigToFile(@NonNull ArrayMap<String, Integer> packageFlags) {
+ private void writeConfigToFile(@NonNull ArrayMap<Pair<Integer, String>, Integer> packageFlags) {
FileOutputStream fos = null;
try {
fos = mConfigFile.startWrite();
@@ -590,13 +794,16 @@
out.startTag(null, "packages");
for (int i = 0; i < packageFlags.size(); i++) {
- final String pkg = packageFlags.keyAt(i);
+ final Pair<Integer, String> key = packageFlags.keyAt(i);
+ final int userId = key.first;
+ final String packageName = key.second;
final int mode = packageFlags.valueAt(i);
if (mode == 0) {
continue;
}
out.startTag(null, "package");
- out.attribute(null, "name", pkg);
+ out.attributeInt(null, "user", userId);
+ out.attribute(null, "name", packageName);
out.attributeInt(null, "flags", mode);
out.endTag(null, "package");
}
@@ -616,7 +823,7 @@
/**
* Reads the configuration file and populates the package flags.
* <p>
- * <strong>Note:</strong> Must be called from the constructor (and thus on the
+ * <strong>Note:</strong> Must be called from #onSystemReady() (and thus on the
* ActivityManagerService thread) since we don't synchronize on config.
*/
private void readConfigFromFileAmsThread() {
@@ -639,21 +846,58 @@
String tagName = parser.getName();
if ("packages".equals(tagName)) {
eventType = parser.next();
+ boolean writeConfigToFileNeeded = false;
do {
if (eventType == XmlPullParser.START_TAG) {
tagName = parser.getName();
if (parser.getDepth() == 2) {
if ("package".equals(tagName)) {
+ final int userId = parser.getAttributeInt(
+ null, "user", USER_NULL);
final String name = parser.getAttributeValue(null, "name");
if (name != null) {
int flagsInt = parser.getAttributeInt(null, "flags", 0);
- mPackageFlags.put(name, flagsInt);
+ if (userId != USER_NULL) {
+ final Pair<Integer, String> packageKey =
+ Pair.create(userId, name);
+ mPackageFlags.put(packageKey, flagsInt);
+ } else {
+ // This is for compatibility with existing configuration
+ // file written from legacy logic(pre-V) which does not have
+ // the flags per-user. (b/296334639)
+ writeConfigToFileNeeded = true;
+ if (!isVisibleBackgroundUsersEnabled()) {
+ // To preserve existing behavior of the devices that
+ // doesn't enable visible background users, populate
+ // the flags for a package as the system user.
+ final Pair<Integer, String> packageKey =
+ Pair.create(USER_SYSTEM, name);
+ mPackageFlags.put(packageKey, flagsInt);
+ } else {
+ // To manage the flags per user in the device that
+ // enable visible background users, populate the flags
+ // for all existing non-profile human user.
+ UserInfo[] users = mUserManagerInternal.getUserInfos();
+ for (UserInfo userInfo : users) {
+ if (!userInfo.isFull()) {
+ continue;
+ }
+ final Pair<Integer, String> packageKey =
+ Pair.create(userInfo.id, name);
+ mPackageFlags.put(packageKey, flagsInt);
+ }
+ }
+ }
}
}
}
}
eventType = parser.next();
} while (eventType != XmlPullParser.END_DOCUMENT);
+
+ if (writeConfigToFileNeeded) {
+ mWriteConfigTask.schedule();
+ }
}
} catch (XmlPullParserException e) {
Slog.w(TAG, "Error reading package metadata", e);
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index f7b4a67..d0c6e15 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -174,27 +174,6 @@
}
}
- // This is needed to bridge the old and new back behavior with recents. While in
- // Overview with live tile enabled, the previous app is technically focused but we
- // add an input consumer to capture all input that would otherwise go to the apps
- // being controlled by the animation. This means that the window resolved is not
- // the right window to consume back while in overview, so we need to route it to
- // launcher and use the legacy behavior of injecting KEYCODE_BACK since the existing
- // compat callback in VRI only works when the window is focused.
- // This symptom also happen while shell transition enabled, we can check that by
- // isTransientLaunch to know whether the focus window is point to live tile.
- final RecentsAnimationController recentsAnimationController =
- wmService.getRecentsAnimationController();
- final ActivityRecord tmpAR = window.mActivityRecord;
- if ((tmpAR != null && tmpAR.isActivityTypeHomeOrRecents()
- && tmpAR.mTransitionController.isTransientLaunch(tmpAR))
- || (recentsAnimationController != null
- && recentsAnimationController.shouldApplyInputConsumer(tmpAR))) {
- ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Current focused window being animated by "
- + "recents. Overriding back callback to recents controller callback.");
- return null;
- }
-
if (!window.isDrawn()) {
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Focused window didn't have a valid surface drawn.");
@@ -832,6 +811,13 @@
return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */);
}
+ boolean shouldPauseTouch(WindowContainer wc) {
+ // Once the close transition is ready, it means the onBackInvoked callback has invoked, and
+ // app is ready to trigger next transition, no matter what it will be.
+ return mAnimationHandler.mComposed && mWaitTransitionFinish == null
+ && mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */);
+ }
+
/**
* Cleanup animation, this can either happen when legacy transition ready, or when the Shell
* transition finish.
@@ -1783,18 +1769,39 @@
}
private void onBackNavigationDone(Bundle result, int backType) {
- boolean triggerBack = result != null && result.getBoolean(
- BackNavigationInfo.KEY_TRIGGER_BACK);
- ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, "
- + "triggerBack=%b", backType, triggerBack);
+ if (result == null) {
+ return;
+ }
+ if (result.containsKey(BackNavigationInfo.KEY_NAVIGATION_FINISHED)) {
+ final boolean triggerBack = result.getBoolean(
+ BackNavigationInfo.KEY_NAVIGATION_FINISHED);
+ ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, "
+ + "triggerBack=%b", backType, triggerBack);
- synchronized (mWindowManagerService.mGlobalLock) {
- mNavigationMonitor.stopMonitorForRemote();
- mBackAnimationInProgress = false;
- mShowWallpaper = false;
- // All animation should be done, clear any un-send animation.
- mPendingAnimation = null;
- mPendingAnimationBuilder = null;
+ synchronized (mWindowManagerService.mGlobalLock) {
+ mNavigationMonitor.stopMonitorForRemote();
+ mBackAnimationInProgress = false;
+ mShowWallpaper = false;
+ // All animation should be done, clear any un-send animation.
+ mPendingAnimation = null;
+ mPendingAnimationBuilder = null;
+ }
+ }
+ if (result.getBoolean(BackNavigationInfo.KEY_GESTURE_FINISHED)) {
+ synchronized (mWindowManagerService.mGlobalLock) {
+ final AnimationHandler ah = mAnimationHandler;
+ if (!ah.mComposed || ah.mWaitTransition || ah.mOpenActivities == null
+ || (ah.mSwitchType != AnimationHandler.TASK_SWITCH
+ && ah.mSwitchType != AnimationHandler.ACTIVITY_SWITCH)) {
+ return;
+ }
+ for (int i = mAnimationHandler.mOpenActivities.length - 1; i >= 0; --i) {
+ final ActivityRecord preDrawActivity = mAnimationHandler.mOpenActivities[i];
+ if (!preDrawActivity.mLaunchTaskBehind) {
+ setLaunchBehind(preDrawActivity);
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 8020516..d70a880 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -109,7 +109,8 @@
this(displayContent, new RemoteMediaProjectionManagerWrapper(displayContent.mDisplayId),
new DisplayManagerFlags().isConnectedDisplayManagementEnabled()
&& !new DisplayManagerFlags()
- .isPixelAnisotropyCorrectionInLogicalDisplayEnabled());
+ .isPixelAnisotropyCorrectionInLogicalDisplayEnabled()
+ && displayContent.getDisplayInfo().type == Display.TYPE_EXTERNAL);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/DeprecatedAbiDialog.java b/services/core/java/com/android/server/wm/DeprecatedAbiDialog.java
index e96208d..46b34a1 100644
--- a/services/core/java/com/android/server/wm/DeprecatedAbiDialog.java
+++ b/services/core/java/com/android/server/wm/DeprecatedAbiDialog.java
@@ -28,8 +28,8 @@
class DeprecatedAbiDialog extends AppWarnings.BaseDialog {
DeprecatedAbiDialog(final AppWarnings manager, Context context,
- ApplicationInfo appInfo) {
- super(manager, appInfo.packageName);
+ ApplicationInfo appInfo, int userId) {
+ super(manager, context, appInfo.packageName, userId);
final PackageManager pm = context.getPackageManager();
final CharSequence label = appInfo.loadSafeLabel(pm,
@@ -41,7 +41,7 @@
final AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setPositiveButton(R.string.ok, (dialog, which) ->
manager.setPackageFlag(
- mPackageName, AppWarnings.FLAG_HIDE_DEPRECATED_ABI, true))
+ mUserId, mPackageName, AppWarnings.FLAG_HIDE_DEPRECATED_ABI, true))
.setMessage(message)
.setTitle(label);
diff --git a/services/core/java/com/android/server/wm/DeprecatedTargetSdkVersionDialog.java b/services/core/java/com/android/server/wm/DeprecatedTargetSdkVersionDialog.java
index 1a7a9b2..ce42385 100644
--- a/services/core/java/com/android/server/wm/DeprecatedTargetSdkVersionDialog.java
+++ b/services/core/java/com/android/server/wm/DeprecatedTargetSdkVersionDialog.java
@@ -31,8 +31,8 @@
class DeprecatedTargetSdkVersionDialog extends AppWarnings.BaseDialog {
DeprecatedTargetSdkVersionDialog(final AppWarnings manager, Context context,
- ApplicationInfo appInfo) {
- super(manager, appInfo.packageName);
+ ApplicationInfo appInfo, int userId) {
+ super(manager, context, appInfo.packageName, userId);
final PackageManager pm = context.getPackageManager();
final CharSequence label = appInfo.loadSafeLabel(pm,
@@ -44,7 +44,7 @@
final AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setPositiveButton(R.string.ok, (dialog, which) ->
manager.setPackageFlag(
- mPackageName, AppWarnings.FLAG_HIDE_DEPRECATED_SDK, true))
+ mUserId, mPackageName, AppWarnings.FLAG_HIDE_DEPRECATED_SDK, true))
.setMessage(message)
.setTitle(label);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 739f76e..00d42e0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -197,6 +197,7 @@
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
import android.util.ArrayMap;
@@ -289,6 +290,9 @@
static final float INVALID_DPI = 0.0f;
+ private final boolean mVisibleBackgroundUserEnabled =
+ UserManager.isVisibleBackgroundUsersEnabled();
+
@IntDef(prefix = { "FORCE_SCALING_MODE_" }, value = {
FORCE_SCALING_MODE_AUTO,
FORCE_SCALING_MODE_DISABLED
@@ -1999,7 +2003,12 @@
}
// Update directly because the app which will change the orientation of display is ready.
if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) {
- sendNewConfiguration();
+ // Run rotation change on display thread. See Transition#shouldApplyOnDisplayThread().
+ mWmService.mH.post(() -> {
+ synchronized (mWmService.mGlobalLock) {
+ sendNewConfiguration();
+ }
+ });
return;
}
if (mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange()) {
@@ -2698,11 +2707,15 @@
* Returns true if the specified UID has access to this display.
*/
boolean hasAccess(int uid) {
- int userId = UserHandle.getUserId(uid);
- boolean isUserVisibleOnDisplay = mWmService.mUmInternal.isUserVisible(
- userId, mDisplayId);
- return mDisplay.hasAccess(uid)
- && (userId == UserHandle.USER_SYSTEM || isUserVisibleOnDisplay);
+ if (!mDisplay.hasAccess(uid)) {
+ return false;
+ }
+ if (!mVisibleBackgroundUserEnabled) {
+ return true;
+ }
+ final int userId = UserHandle.getUserId(uid);
+ return userId == UserHandle.USER_SYSTEM
+ || mWmService.mUmInternal.isUserVisible(userId, mDisplayId);
}
boolean isPrivate() {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 5e0d4f9..4544572 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1939,6 +1939,11 @@
*/
final Rect mOverrideConfigInsets = new Rect();
+ /**
+ * Override value of mNonDecorInsets for app compatibility purpose.
+ */
+ final Rect mOverrideNonDecorInsets = new Rect();
+
/** The display frame available after excluding {@link #mNonDecorInsets}. */
final Rect mNonDecorFrame = new Rect();
@@ -1954,6 +1959,11 @@
*/
final Rect mOverrideConfigFrame = new Rect();
+ /**
+ * Override value of mNonDecorFrame for app compatibility purpose.
+ */
+ final Rect mOverrideNonDecorFrame = new Rect();
+
private boolean mNeedUpdate = true;
InsetsState update(DisplayContent dc, int rotation, int w, int h) {
@@ -1973,17 +1983,26 @@
? configInsets
: insetsState.calculateInsets(displayFrame,
dc.mWmService.mOverrideConfigTypes, true /* ignoreVisibility */);
+ final Insets overrideDecorInsets = dc.mWmService.mDecorTypes
+ == dc.mWmService.mOverrideDecorTypes
+ ? decor
+ : insetsState.calculateInsets(displayFrame,
+ dc.mWmService.mOverrideDecorTypes, true /* ignoreVisibility */);
mNonDecorInsets.set(decor.left, decor.top, decor.right, decor.bottom);
mConfigInsets.set(configInsets.left, configInsets.top, configInsets.right,
configInsets.bottom);
mOverrideConfigInsets.set(overrideConfigInsets.left, overrideConfigInsets.top,
overrideConfigInsets.right, overrideConfigInsets.bottom);
+ mOverrideNonDecorInsets.set(overrideDecorInsets.left, overrideDecorInsets.top,
+ overrideDecorInsets.right, overrideDecorInsets.bottom);
mNonDecorFrame.set(displayFrame);
mNonDecorFrame.inset(mNonDecorInsets);
mConfigFrame.set(displayFrame);
mConfigFrame.inset(mConfigInsets);
mOverrideConfigFrame.set(displayFrame);
mOverrideConfigFrame.inset(mOverrideConfigInsets);
+ mOverrideNonDecorFrame.set(displayFrame);
+ mOverrideNonDecorFrame.inset(mOverrideNonDecorInsets);
mNeedUpdate = false;
return insetsState;
}
@@ -1992,9 +2011,11 @@
mNonDecorInsets.set(other.mNonDecorInsets);
mConfigInsets.set(other.mConfigInsets);
mOverrideConfigInsets.set(other.mOverrideConfigInsets);
+ mOverrideNonDecorInsets.set(other.mOverrideNonDecorInsets);
mNonDecorFrame.set(other.mNonDecorFrame);
mConfigFrame.set(other.mConfigFrame);
mOverrideConfigFrame.set(other.mOverrideConfigFrame);
+ mOverrideNonDecorFrame.set(other.mOverrideNonDecorFrame);
mNeedUpdate = false;
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index a8cbc62..8858766 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -519,8 +519,17 @@
final SurfaceControl leash = mAdapter.mCapturedLeash;
mControlTarget = target;
updateVisibility();
+ boolean initiallyVisible = mClientVisible;
+ if (mSource.getType() == WindowInsets.Type.ime()) {
+ // The IME cannot be initially visible, see ControlAdapter#startAnimation below.
+ // Also, the ImeInsetsSourceConsumer clears the client visibility upon losing control,
+ // but this won't have reached here yet by the time the new control is created.
+ // Note: The DisplayImeController needs the correct previous client's visibility, so we
+ // only override the initiallyVisible here.
+ initiallyVisible = false;
+ }
mControl = new InsetsSourceControl(mSource.getId(), mSource.getType(), leash,
- mClientVisible, surfacePosition, getInsetsHint());
+ initiallyVisible, surfacePosition, getInsetsHint());
ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
"InsetsSource Control %s for target %s", mControl, mControlTarget);
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 45cf10b..5aa0ed7 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -327,14 +327,14 @@
R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
- mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
- R.dimen.config_letterboxHorizontalPositionMultiplier);
- mLetterboxVerticalPositionMultiplier = mContext.getResources().getFloat(
- R.dimen.config_letterboxVerticalPositionMultiplier);
- mLetterboxBookModePositionMultiplier = mContext.getResources().getFloat(
- R.dimen.config_letterboxBookModePositionMultiplier);
- mLetterboxTabletopModePositionMultiplier = mContext.getResources().getFloat(
- R.dimen.config_letterboxTabletopModePositionMultiplier);
+ setLetterboxHorizontalPositionMultiplier(mContext.getResources().getFloat(
+ R.dimen.config_letterboxHorizontalPositionMultiplier));
+ setLetterboxVerticalPositionMultiplier(mContext.getResources().getFloat(
+ R.dimen.config_letterboxVerticalPositionMultiplier));
+ setLetterboxBookModePositionMultiplier(mContext.getResources().getFloat(
+ R.dimen.config_letterboxBookModePositionMultiplier));
+ setLetterboxTabletopModePositionMultiplier(mContext.getResources()
+ .getFloat(R.dimen.config_letterboxTabletopModePositionMultiplier));
mIsHorizontalReachabilityEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsHorizontalReachabilityEnabled);
mIsVerticalReachabilityEnabled = mContext.getResources().getBoolean(
@@ -657,29 +657,8 @@
* right side.
*/
float getLetterboxHorizontalPositionMultiplier(boolean isInBookMode) {
- if (isInBookMode) {
- if (mLetterboxBookModePositionMultiplier < 0.0f
- || mLetterboxBookModePositionMultiplier > 1.0f) {
- Slog.w(TAG,
- "mLetterboxBookModePositionMultiplier out of bounds (isInBookMode=true): "
- + mLetterboxBookModePositionMultiplier);
- // Default to left position if invalid value is provided.
- return 0.0f;
- } else {
- return mLetterboxBookModePositionMultiplier;
- }
- } else {
- if (mLetterboxHorizontalPositionMultiplier < 0.0f
- || mLetterboxHorizontalPositionMultiplier > 1.0f) {
- Slog.w(TAG,
- "mLetterboxBookModePositionMultiplier out of bounds (isInBookMode=false):"
- + mLetterboxBookModePositionMultiplier);
- // Default to central position if invalid value is provided.
- return 0.5f;
- } else {
- return mLetterboxHorizontalPositionMultiplier;
- }
- }
+ return isInBookMode ? mLetterboxBookModePositionMultiplier
+ : mLetterboxHorizontalPositionMultiplier;
}
/*
@@ -689,37 +668,28 @@
* bottom side.
*/
float getLetterboxVerticalPositionMultiplier(boolean isInTabletopMode) {
- if (isInTabletopMode) {
- return (mLetterboxTabletopModePositionMultiplier < 0.0f
- || mLetterboxTabletopModePositionMultiplier > 1.0f)
- // Default to top position if invalid value is provided.
- ? 0.0f : mLetterboxTabletopModePositionMultiplier;
- } else {
- return (mLetterboxVerticalPositionMultiplier < 0.0f
- || mLetterboxVerticalPositionMultiplier > 1.0f)
- // Default to central position if invalid value is provided.
- ? 0.5f : mLetterboxVerticalPositionMultiplier;
- }
+ return isInTabletopMode ? mLetterboxTabletopModePositionMultiplier
+ : mLetterboxVerticalPositionMultiplier;
}
/**
- * Overrides horizontal position of a center of the letterboxed app window. If given value < 0
- * or > 1, then it and a value of {@link
- * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and
- * central position (0.5) is used.
+ * Overrides horizontal position of a center of the letterboxed app window.
+ *
+ * @throws IllegalArgumentException If given value < 0 or > 1.
*/
void setLetterboxHorizontalPositionMultiplier(float multiplier) {
- mLetterboxHorizontalPositionMultiplier = multiplier;
+ mLetterboxHorizontalPositionMultiplier = assertValidMultiplier(multiplier,
+ "mLetterboxHorizontalPositionMultiplier");
}
/**
- * Overrides vertical position of a center of the letterboxed app window. If given value < 0
- * or > 1, then it and a value of {@link
- * com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier} are ignored and
- * central position (0.5) is used.
+ * Overrides vertical position of a center of the letterboxed app window.
+ *
+ * @throws IllegalArgumentException If given value < 0 or > 1.
*/
void setLetterboxVerticalPositionMultiplier(float multiplier) {
- mLetterboxVerticalPositionMultiplier = multiplier;
+ mLetterboxVerticalPositionMultiplier = assertValidMultiplier(multiplier,
+ "mLetterboxVerticalPositionMultiplier");
}
/**
@@ -740,6 +710,28 @@
com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier);
}
+ /**
+ * Sets tabletop mode position multiplier.
+ *
+ * @throws IllegalArgumentException If given value < 0 or > 1.
+ */
+ @VisibleForTesting
+ void setLetterboxTabletopModePositionMultiplier(float multiplier) {
+ mLetterboxTabletopModePositionMultiplier = assertValidMultiplier(multiplier,
+ "mLetterboxTabletopModePositionMultiplier");
+ }
+
+ /**
+ * Sets tabletop mode position multiplier.
+ *
+ * @throws IllegalArgumentException If given value < 0 or > 1.
+ */
+ @VisibleForTesting
+ void setLetterboxBookModePositionMultiplier(float multiplier) {
+ mLetterboxBookModePositionMultiplier = assertValidMultiplier(multiplier,
+ "mLetterboxBookModePositionMultiplier");
+ }
+
/*
* Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps in
* landscape device orientation.
@@ -1356,4 +1348,21 @@
void resetUserAppAspectRatioFullscreenEnabled() {
setUserAppAspectRatioFullscreenOverrideEnabled(false);
}
+
+ /**
+ * Checks whether the multiplier is between [0,1].
+ *
+ * @param multiplierName sent in the exception if multiplier is invalid, for easier debugging.
+ *
+ * @return multiplier, if valid
+ * @throws IllegalArgumentException if outside bounds.
+ */
+ private float assertValidMultiplier(float multiplier, String multiplierName)
+ throws IllegalArgumentException {
+ if (multiplier < 0.0f || multiplier > 1.0f) {
+ throw new IllegalArgumentException("Trying to set " + multiplierName
+ + " out of bounds: " + multiplier);
+ }
+ return multiplier;
+ }
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 3f24545..f220c9d 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -1084,6 +1084,10 @@
|| mUserAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN);
}
+ boolean hasFullscreenOverride() {
+ return isSystemOverrideToFullscreenEnabled() || shouldApplyUserFullscreenOverride();
+ }
+
float getUserMinAspectRatio() {
switch (mUserAspectRatio) {
case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE:
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 9dba8c6..1e88fe4 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -72,7 +72,6 @@
import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
import static com.android.server.wm.Task.REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
-import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -270,8 +269,6 @@
private int mTmpTaskLayerRank;
private final RankTaskLayersRunnable mRankTaskLayersRunnable = new RankTaskLayersRunnable();
- private final AttachApplicationHelper mAttachApplicationHelper = new AttachApplicationHelper();
-
private String mDestroyAllActivitiesReason;
private final Runnable mDestroyAllActivitiesRunnable = new Runnable() {
@Override
@@ -1838,11 +1835,39 @@
}
boolean attachApplication(WindowProcessController app) throws RemoteException {
- try {
- return mAttachApplicationHelper.process(app);
- } finally {
- mAttachApplicationHelper.reset();
+ final ArrayList<ActivityRecord> activities = mService.mStartingProcessActivities;
+ RemoteException remoteException = null;
+ boolean hasActivityStarted = false;
+ for (int i = activities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = activities.get(i);
+ if (app.mUid != r.info.applicationInfo.uid || !app.mName.equals(r.processName)) {
+ // The attaching process does not match the starting activity.
+ continue;
+ }
+ // Consume the pending record.
+ activities.remove(i);
+ final TaskFragment tf = r.getTaskFragment();
+ if (tf == null || r.finishing || r.app != null
+ // Ignore keyguard because the app may use show-when-locked when creating.
+ || !r.shouldBeVisible(true /* ignoringKeyguard */)
+ || !r.showToCurrentUser()) {
+ continue;
+ }
+ try {
+ final boolean canResume = r.isFocusable() && r == tf.topRunningActivity();
+ if (mTaskSupervisor.realStartActivityLocked(r, app, canResume,
+ true /* checkConfig */)) {
+ hasActivityStarted = true;
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception in new process when starting " + r, e);
+ remoteException = e;
+ }
}
+ if (remoteException != null) {
+ throw remoteException;
+ }
+ return hasActivityStarted;
}
/**
@@ -2244,9 +2269,7 @@
newTransition.setReady(rootTask, true /* ready */);
}
- if (!isPip2ExperimentEnabled()) {
- resumeFocusedTasksTopActivities();
- }
+ resumeFocusedTasksTopActivities();
notifyActivityPipModeChanged(r.getTask(), r);
}
@@ -2380,6 +2403,14 @@
return false;
}
+ return resumeFocusedTasksTopActivitiesUnchecked(targetRootTask, target, targetOptions,
+ deferPause);
+ }
+
+ @VisibleForTesting
+ boolean resumeFocusedTasksTopActivitiesUnchecked(
+ Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions,
+ boolean deferPause) {
boolean result = false;
if (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea()
|| getTopDisplayFocusedRootTask() == targetRootTask)) {
@@ -3742,67 +3773,4 @@
}
}
}
-
- private class AttachApplicationHelper implements Consumer<Task>, Predicate<ActivityRecord> {
- private boolean mHasActivityStarted;
- private RemoteException mRemoteException;
- private WindowProcessController mApp;
- private ActivityRecord mTop;
-
- void reset() {
- mHasActivityStarted = false;
- mRemoteException = null;
- mApp = null;
- mTop = null;
- }
-
- boolean process(WindowProcessController app) throws RemoteException {
- mApp = app;
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- getChildAt(displayNdx).forAllRootTasks(this);
- if (mRemoteException != null) {
- throw mRemoteException;
- }
- }
- if (!mHasActivityStarted) {
- ensureActivitiesVisible();
- }
- return mHasActivityStarted;
- }
-
- @Override
- public void accept(Task rootTask) {
- if (mRemoteException != null) {
- return;
- }
- if (rootTask.getVisibility(null /* starting */)
- == TASK_FRAGMENT_VISIBILITY_INVISIBLE) {
- return;
- }
- mTop = rootTask.topRunningActivity();
- rootTask.forAllActivities(this);
- }
-
- @Override
- public boolean test(ActivityRecord r) {
- if (r.finishing || !r.showToCurrentUser() || !r.visibleIgnoringKeyguard
- || r.app != null || mApp.mUid != r.info.applicationInfo.uid
- || !mApp.mName.equals(r.processName)) {
- return false;
- }
-
- try {
- if (mTaskSupervisor.realStartActivityLocked(r, mApp,
- mTop == r && r.getTask().canBeResumed(r) /* andResume */,
- true /* checkConfig */)) {
- mHasActivityStarted = true;
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception in new application when starting activity " + mTop, e);
- mRemoteException = e;
- return true;
- }
- return false;
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index e157318..f8aa69b 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -912,7 +912,7 @@
@Override
public void grantInputChannel(int displayId, SurfaceControl surface,
IBinder clientToken, @Nullable InputTransferToken hostInputTransferToken, int flags,
- int privateFlags, int type, int inputFeatures, IBinder windowToken,
+ int privateFlags, int inputFeatures, int type, IBinder windowToken,
InputTransferToken inputTransferToken, String inputHandleName,
InputChannel outInputChannel) {
if (hostInputTransferToken == null && !mCanAddInternalSystemWindow) {
@@ -925,7 +925,7 @@
try {
mService.grantInputChannel(this, mUid, mPid, displayId, surface, clientToken,
hostInputTransferToken, flags, mCanAddInternalSystemWindow ? privateFlags : 0,
- type, inputFeatures, windowToken, inputTransferToken, inputHandleName,
+ inputFeatures, type, windowToken, inputTransferToken, inputHandleName,
outInputChannel);
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 129af90..8042010 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2188,38 +2188,20 @@
return getTask() != null ? getTask().mTaskId : INVALID_TASK_ID;
}
+ static class ConfigOverrideHint {
+ @Nullable DisplayInfo mTmpOverrideDisplayInfo;
+ @Nullable ActivityRecord.CompatDisplayInsets mTmpCompatInsets;
+ boolean mUseLegacyInsetsForStableBounds;
+ }
+
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
@NonNull Configuration parentConfig) {
- computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
- null /* compatInsets */);
- }
-
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
- if (overrideDisplayInfo != null) {
- // Make sure the screen related configs can be computed by the provided display info.
- inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
- invalidateAppBoundsConfig(inOutConfig);
- }
- computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
- null /* compatInsets */);
- }
-
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig,
- @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
- if (compatInsets != null) {
- // Make sure the app bounds can be computed by the compat insets.
- invalidateAppBoundsConfig(inOutConfig);
- }
- computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
- compatInsets);
+ computeConfigResourceOverrides(inOutConfig, parentConfig, null /* configOverrideHint */);
}
/**
* Forces the app bounds related configuration can be computed by
- * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
- * ActivityRecord.CompatDisplayInsets)}.
+ * {@link #computeConfigResourceOverrides(Configuration, Configuration, ConfigOverrideHint)}.
*/
private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
@@ -2239,8 +2221,24 @@
* just be inherited from the parent configuration.
**/
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo,
- @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
+ @NonNull Configuration parentConfig, @Nullable ConfigOverrideHint overrideHint) {
+ DisplayInfo overrideDisplayInfo = null;
+ ActivityRecord.CompatDisplayInsets compatInsets = null;
+ boolean useLegacyInsetsForStableBounds = false;
+ if (overrideHint != null) {
+ overrideDisplayInfo = overrideHint.mTmpOverrideDisplayInfo;
+ compatInsets = overrideHint.mTmpCompatInsets;
+ useLegacyInsetsForStableBounds = overrideHint.mUseLegacyInsetsForStableBounds;
+ if (overrideDisplayInfo != null) {
+ // Make sure the screen related configs can be computed by the provided
+ // display info.
+ inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
+ }
+ if (overrideDisplayInfo != null || compatInsets != null) {
+ // Make sure the app bounds can be computed by the compat insets.
+ invalidateAppBoundsConfig(inOutConfig);
+ }
+ }
int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
windowingMode = parentConfig.windowConfiguration.getWindowingMode();
@@ -2310,7 +2308,7 @@
// The non decor inset are areas that could never be removed in Honeycomb. See
// {@link WindowManagerPolicy#getNonDecorInsetsLw}.
calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di,
- false /* useLegacyInsetsForStableBounds */);
+ useLegacyInsetsForStableBounds);
} else {
// Apply the given non-decor and stable insets to calculate the corresponding bounds
// for screen size of configuration.
@@ -2423,11 +2421,12 @@
final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
final DisplayPolicy.DecorInsets.Info info = policy.getDecorInsetsInfo(
displayInfo.rotation, displayInfo.logicalWidth, displayInfo.logicalHeight);
- intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, info.mNonDecorInsets);
if (!useLegacyInsetsForStableBounds) {
intersectWithInsetsIfFits(outStableBounds, mTmpBounds, info.mConfigInsets);
+ intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, info.mNonDecorInsets);
} else {
intersectWithInsetsIfFits(outStableBounds, mTmpBounds, info.mOverrideConfigInsets);
+ intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, info.mOverrideNonDecorInsets);
}
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 222abc3..ce53290 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -565,6 +565,9 @@
if (isTransientCollect(ar)) {
return true;
}
+ for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) {
+ if (mWaitingTransitions.get(i).isTransientLaunch(ar)) return true;
+ }
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
if (mPlayingTransitions.get(i).isTransientLaunch(ar)) return true;
}
diff --git a/services/core/java/com/android/server/wm/UnsupportedCompileSdkDialog.java b/services/core/java/com/android/server/wm/UnsupportedCompileSdkDialog.java
index f376e8b..0655068 100644
--- a/services/core/java/com/android/server/wm/UnsupportedCompileSdkDialog.java
+++ b/services/core/java/com/android/server/wm/UnsupportedCompileSdkDialog.java
@@ -32,8 +32,8 @@
class UnsupportedCompileSdkDialog extends AppWarnings.BaseDialog {
UnsupportedCompileSdkDialog(final AppWarnings manager, Context context,
- ApplicationInfo appInfo) {
- super(manager, appInfo.packageName);
+ ApplicationInfo appInfo, int userId) {
+ super(manager, context, appInfo.packageName, userId);
final PackageManager pm = context.getPackageManager();
final CharSequence label = appInfo.loadSafeLabel(pm,
@@ -68,6 +68,6 @@
final CheckBox alwaysShow = mDialog.findViewById(R.id.ask_checkbox);
alwaysShow.setChecked(true);
alwaysShow.setOnCheckedChangeListener((buttonView, isChecked) -> manager.setPackageFlag(
- mPackageName, AppWarnings.FLAG_HIDE_COMPILE_SDK, !isChecked));
+ mUserId, mPackageName, AppWarnings.FLAG_HIDE_COMPILE_SDK, !isChecked));
}
}
diff --git a/services/core/java/com/android/server/wm/UnsupportedDisplaySizeDialog.java b/services/core/java/com/android/server/wm/UnsupportedDisplaySizeDialog.java
index b11c22d..5e40d9c 100644
--- a/services/core/java/com/android/server/wm/UnsupportedDisplaySizeDialog.java
+++ b/services/core/java/com/android/server/wm/UnsupportedDisplaySizeDialog.java
@@ -30,8 +30,8 @@
class UnsupportedDisplaySizeDialog extends AppWarnings.BaseDialog {
UnsupportedDisplaySizeDialog(final AppWarnings manager, Context context,
- ApplicationInfo appInfo) {
- super(manager, appInfo.packageName);
+ ApplicationInfo appInfo, int userId) {
+ super(manager, context, appInfo.packageName, userId);
final PackageManager pm = context.getPackageManager();
final CharSequence label = appInfo.loadSafeLabel(pm,
@@ -59,6 +59,6 @@
final CheckBox alwaysShow = mDialog.findViewById(R.id.ask_checkbox);
alwaysShow.setChecked(true);
alwaysShow.setOnCheckedChangeListener((buttonView, isChecked) -> manager.setPackageFlag(
- mPackageName, AppWarnings.FLAG_HIDE_DISPLAY_SIZE, !isChecked));
+ mUserId, mPackageName, AppWarnings.FLAG_HIDE_DISPLAY_SIZE, !isChecked));
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 5c24eee..9d1551c 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -193,11 +193,11 @@
if (mVisibleRequested != visible) {
// Before setting mVisibleRequested so we can track changes.
final WindowState wpTarget = mDisplayContent.mWallpaperController.getWallpaperTarget();
- final boolean isTargetNotCollectedActivity = wpTarget != null
- && wpTarget.mActivityRecord != null
- && !mTransitionController.isCollecting(wpTarget.mActivityRecord);
- // Skip collecting requesting-invisible wallpaper if the wallpaper target is an activity
- // and it is not collected. Because the visibility change may be called after the
+ final boolean isTargetNotCollectedActivity = wpTarget == null
+ || (wpTarget.mActivityRecord != null
+ && !mTransitionController.isCollecting(wpTarget.mActivityRecord));
+ // Skip collecting requesting-invisible wallpaper if the wallpaper target is empty or
+ // a non-collected activity. Because the visibility change may be called after the
// transition of activity is finished, e.g. WallpaperController#hideWallpapers from
// hiding surface of the target. Then if there is a next transition, the wallpaper
// change may be collected into the unrelated transition and cause a weird animation.
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index b43a454..8afcf0e 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -150,7 +150,9 @@
dc.checkAppWindowsReadyToShow();
}
if (accessibilityController.hasCallbacks()) {
- accessibilityController.drawMagnifiedRegionBorderIfNeeded(dc.mDisplayId);
+ accessibilityController
+ .recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded(
+ dc.mDisplayId);
}
if (dc.isAnimating(animationFlags, ANIMATION_TYPE_ALL)) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 74616ac..1496ae0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -241,6 +241,7 @@
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
+import android.util.IntArray;
import android.util.MergedConfiguration;
import android.util.Pair;
import android.util.Slog;
@@ -569,6 +570,8 @@
final int mOverrideConfigTypes;
+ final int mOverrideDecorTypes;
+
final boolean mLimitedAlphaCompositing;
final int mMaxUiWidth;
@@ -1106,6 +1109,14 @@
@GuardedBy("mGlobalLock")
final SensitiveContentPackages mSensitiveContentPackages = new SensitiveContentPackages();
+ /**
+ * UIDs for which a Toast has been shown to indicate
+ * {@link LocalService#addBlockScreenCaptureForApps(ArraySet) screen capture blocking}. This is
+ * used to ensure we don't keep re-showing the Toast every time the window becomes visible.
+ * UIDs are removed when the app is removed from the block list.
+ */
+ @GuardedBy("mGlobalLock")
+ private final IntArray mCaptureBlockedToastShownUids = new IntArray();
/** Listener to notify activity manager about app transitions. */
final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier
@@ -1246,10 +1257,13 @@
if (isScreenSizeDecoupledFromStatusBarAndCutout && !mFlags.mInsetsDecoupledConfiguration) {
// If the global new behavior is not there, but the partial decouple flag is on.
mOverrideConfigTypes = 0;
+ mOverrideDecorTypes = 0;
} else {
mOverrideConfigTypes =
WindowInsets.Type.displayCutout() | WindowInsets.Type.statusBars()
| WindowInsets.Type.navigationBars();
+ mOverrideDecorTypes = WindowInsets.Type.displayCutout()
+ | WindowInsets.Type.navigationBars();
}
mLetterboxConfiguration = new LetterboxConfiguration(
@@ -2437,6 +2451,9 @@
ProtoLog.i(WM_DEBUG_SCREEN_ON,
"Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility,
viewVisibility, new RuntimeException().fillInStackTrace());
+ if (becameVisible) {
+ onWindowVisible(win);
+ }
win.setDisplayLayoutNeeded();
win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
@@ -8746,6 +8763,15 @@
if (modified) {
WindowManagerService.this.refreshScreenCaptureDisabled();
}
+ if (sensitiveContentImprovements()) {
+ for (int i = 0; i < packageInfos.size(); i++) {
+ int uid = packageInfos.valueAt(i).getUid();
+ if (mCaptureBlockedToastShownUids.contains(uid)) {
+ mCaptureBlockedToastShownUids.remove(
+ mCaptureBlockedToastShownUids.indexOf(uid));
+ }
+ }
+ }
}
}
@@ -8756,6 +8782,9 @@
if (modified) {
WindowManagerService.this.refreshScreenCaptureDisabled();
}
+ if (sensitiveContentImprovements()) {
+ mCaptureBlockedToastShownUids.clear();
+ }
}
}
@@ -10147,7 +10176,7 @@
* Called to notify WMS that the specified window has become visible. This shows a Toast if the
* window is deemed to hold sensitive content.
*/
- void onWindowVisible(@NonNull WindowState w) {
+ private void onWindowVisible(@NonNull WindowState w) {
showToastIfBlockingScreenCapture(w);
}
@@ -10157,9 +10186,13 @@
* on sensitive content protections.
*/
private void showToastIfBlockingScreenCapture(@NonNull WindowState w) {
- // TODO(b/323580163): Check if already shown and update shown state.
- if (mSensitiveContentPackages.shouldBlockScreenCaptureForApp(w.getOwningPackage(),
- w.getOwningUid(), w.getWindowToken())) {
+ int uid = w.getOwningUid();
+ if (mCaptureBlockedToastShownUids.contains(uid)) {
+ return;
+ }
+ if (mSensitiveContentPackages.shouldBlockScreenCaptureForApp(w.getOwningPackage(), uid,
+ w.getWindowToken())) {
+ mCaptureBlockedToastShownUids.add(uid);
mH.post(() -> {
Toast.makeText(mContext, Looper.getMainLooper(),
mContext.getString(R.string.screen_not_shared_sensitive_content),
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 731184f..1f06bfa 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -848,7 +848,12 @@
return -1;
}
synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier);
+ try {
+ mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier);
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Error: invalid multiplier value " + e);
+ return -1;
+ }
}
return 0;
}
@@ -867,7 +872,12 @@
return -1;
}
synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(multiplier);
+ try {
+ mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(multiplier);
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Error: invalid multiplier value " + e);
+ return -1;
+ }
}
return 0;
}
@@ -1539,9 +1549,9 @@
pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will");
pw.println(" be ignored and framework implementation will determine aspect ratio.");
pw.println(" --cornerRadius radius");
- pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
- pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
- pw.println(" ignored and corners of the activity won't be rounded.");
+ pw.println(" Corners radius (in pixels) for activities in the letterbox mode.");
+ pw.println(" If radius < 0, both R.integer.config_letterboxActivityCornersRadius");
+ pw.println(" and it will be ignored and corners of the activity won't be rounded.");
pw.println(" --backgroundType [reset|solid_color|app_color_background");
pw.println(" |app_color_background_floating|wallpaper]");
pw.println(" Type of background used in the letterbox mode.");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 14ec41f..8c9317a 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -67,7 +67,9 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
+import static com.android.server.wm.ActivityTaskManagerService.isPip2ExperimentEnabled;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
@@ -828,18 +830,20 @@
}
private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) {
+ final boolean wasPrevFocusableAndVisible = tr.isFocusableAndVisible();
+
int effects = applyChanges(tr, c);
final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
- effects = TRANSACT_EFFECTS_LIFECYCLE;
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
}
if ((c.getChangeMask() & CHANGE_FORCE_TRANSLUCENT) != 0) {
tr.setForceTranslucent(c.getForceTranslucent());
- effects = TRANSACT_EFFECTS_LIFECYCLE;
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING) != 0) {
@@ -872,8 +876,17 @@
boolean canEnterPip = activity.checkEnterPictureInPictureState(
"applyTaskChanges", true /* beforeStopping */);
if (canEnterPip) {
- canEnterPip = mService.mActivityClientController
- .requestPictureInPictureMode(activity);
+ mService.mTaskSupervisor.beginDeferResume();
+ try {
+ canEnterPip = mService.mActivityClientController
+ .requestPictureInPictureMode(activity);
+ } finally {
+ mService.mTaskSupervisor.endDeferResume();
+ if (canEnterPip && !isPip2ExperimentEnabled()) {
+ // Wait until the transaction is applied to only resume once.
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ }
}
if (!canEnterPip) {
// Restore the flag to its previous state when the activity cannot enter PIP.
@@ -882,6 +895,11 @@
}
}
+ // Activity in this Task may resume/pause when enter/exit pip.
+ if (wasPrevFocusableAndVisible != tr.isFocusableAndVisible()) {
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+
return effects;
}
@@ -947,7 +965,7 @@
}
if ((c.getChangeMask() & CHANGE_FORCE_TRANSLUCENT) != 0) {
taskFragment.setForceTranslucent(c.getForceTranslucent());
- effects = TRANSACT_EFFECTS_LIFECYCLE;
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
effects |= applyChanges(taskFragment, c);
@@ -1225,17 +1243,32 @@
ActivityRecord pipActivity = pipTask.getActivity(
(activity) -> activity.pictureInPictureArgs != null);
+ if (pipActivity.isState(RESUMED)) {
+ // schedulePauseActivity() call uses this flag when entering PiP after Recents
+ // swipe-up TO_FRONT transition. In this case the state of the activity is
+ // RESUMED until ActivityRecord#makeActiveIfNeeded() makes it PAUSING followed
+ // by the scheduling for PAUSE. See moveActivityToPinnedRootTask()'s call into
+ // resumeFocusedTasksTopActivities().
+ pipActivity.mAutoEnteringPip =
+ pipActivity.pictureInPictureArgs.isAutoEnterEnabled();
+ }
Rect entryBounds = hop.getBounds();
mService.mRootWindowContainer.moveActivityToPinnedRootTask(
pipActivity, null /* launchIntoPipHostActivity */,
"moveActivityToPinnedRootTask", null /* transition */, entryBounds);
- // Continue the pausing process after potential task reparenting.
if (pipActivity.isState(PAUSING) && pipActivity.mPauseSchedulePendingForPip) {
+ // Continue the pausing process. This must be done after moving PiP activity to
+ // a potentially new pinned task (multi-activity case). This case is only
+ // triggered if TaskFragment#startPausing() deems this an auto-enter case;
+ // i.e. we enter this flow during button-nav auto-enter but not gesture-nav
+ // auto-enter PiP for example.
pipActivity.getTask().schedulePauseActivity(
pipActivity, false /* userLeaving */,
false /* pauseImmediately */, true /* autoEnteringPip */, "auto-pip");
}
+ // Reset auto-entering PiP info since any internal state updates are finished.
+ pipActivity.mAutoEnteringPip = false;
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
@@ -1367,10 +1400,10 @@
final IBinder callerActivityToken = operation.getActivityToken();
final Intent activityIntent = operation.getActivityIntent();
final Bundle activityOptions = operation.getBundle();
- final int result = mService.getActivityStartController()
+ final int result = waitAsyncStart(() -> mService.getActivityStartController()
.startActivityInTaskFragment(taskFragment, activityIntent, activityOptions,
callerActivityToken, caller.mUid, caller.mPid,
- errorCallbackToken);
+ errorCallbackToken));
if (!isStartResultSuccessful(result)) {
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
opType, convertStartFailureToThrowable(result, activityIntent));
@@ -2277,6 +2310,9 @@
TaskFragmentOrganizerToken organizerToken = creationParams.getOrganizer();
taskFragment.setTaskFragmentOrganizer(organizerToken,
ownerActivity.getUid(), ownerActivity.info.processName);
+ if (mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken.asBinder())) {
+ taskFragment.setOverrideOrientation(creationParams.getOverrideOrientation());
+ }
final int position;
if (creationParams.getPairedPrimaryFragmentToken() != null) {
// When there is a paired primary TaskFragment, we want to place the new TaskFragment
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7a0245b..2fcee50 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -28,7 +28,6 @@
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.permission.flags.Flags.sensitiveContentImprovements;
import static android.view.SurfaceControl.Transaction;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
@@ -2139,9 +2138,6 @@
}
}
setDisplayLayoutNeeded();
- if (sensitiveContentImprovements() && visible) {
- mWmService.onWindowVisible(this);
- }
}
}
@@ -2974,6 +2970,11 @@
return true;
}
+ // Do not allow back predictive animation target to receive touch, app can trigger an
+ // unexpected transition so basically unable to polish it.
+ if (mWmService.mAtmService.mBackNavigationController.shouldPauseTouch(mActivityRecord)) {
+ return false;
+ }
return !mActivityRecord.getTask().getRootTask().shouldIgnoreInput()
&& mActivityRecord.isVisibleRequested();
}
@@ -3701,22 +3702,11 @@
mDragResizingChangeReported = true;
mWindowFrames.clearReportResizeHints();
- // App window resize may trigger Activity#onConfigurationChanged, so we need to update
- // ActivityWindowInfo as well.
- final IBinder activityToken;
- final ActivityWindowInfo activityWindowInfo;
- if (mLastReportedActivityWindowInfo != null) {
- activityToken = mActivityRecord.token;
- activityWindowInfo = mLastReportedActivityWindowInfo;
- } else {
- activityToken = null;
- activityWindowInfo = null;
- }
-
final int prevRotation = mLastReportedConfiguration
.getMergedConfiguration().windowConfiguration.getRotation();
fillClientWindowFramesAndConfiguration(mClientWindowFrames, mLastReportedConfiguration,
- activityWindowInfo, true /* useLatestConfig */, false /* relayoutVisible */);
+ mLastReportedActivityWindowInfo, true /* useLatestConfig */,
+ false /* relayoutVisible */);
final boolean syncRedraw = shouldSendRedrawForSync();
final boolean syncWithBuffers = syncRedraw && shouldSyncWithBuffers();
final boolean reportDraw = syncRedraw || drawPending;
@@ -3740,14 +3730,15 @@
mLastReportedConfiguration, getCompatInsetsState(), forceRelayout,
alwaysConsumeSystemBars, displayId,
syncWithBuffers ? mSyncSeqId : -1, isDragResizing,
- activityToken, activityWindowInfo));
+ mLastReportedActivityWindowInfo));
onResizePostDispatched(drawPending, prevRotation, displayId);
} else {
// TODO(b/301870955): cleanup after launch
try {
mClient.resized(mClientWindowFrames, reportDraw, mLastReportedConfiguration,
getCompatInsetsState(), forceRelayout, alwaysConsumeSystemBars, displayId,
- syncWithBuffers ? mSyncSeqId : -1, isDragResizing, activityWindowInfo);
+ syncWithBuffers ? mSyncSeqId : -1, isDragResizing,
+ mLastReportedActivityWindowInfo);
onResizePostDispatched(drawPending, prevRotation, displayId);
} catch (RemoteException e) {
// Cancel orientation change of this window to avoid blocking unfreeze display.
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index b999305f..736b051 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -1,12 +1,8 @@
-# Display
-per-file com_android_server_lights_LightsService.cpp = michaelwr@google.com, santoscordon@google.com
-
# Input
per-file com_android_server_input_* = file:/INPUT_OWNERS
# Power
-per-file com_android_server_HardwarePropertiesManagerService.cpp = michaelwr@google.com, santoscordon@google.com
-per-file com_android_server_power_PowerManagerService.* = michaelwr@google.com, santoscordon@google.com
+per-file com_android_server_HardwarePropertiesManagerService.cpp = file:/services/core/java/com/android/server/power/OWNERS
# BatteryStats
per-file com_android_server_am_BatteryStatsService.cpp = file:/BATTERY_STATS_OWNERS
@@ -16,6 +12,7 @@
per-file com_android_server_Usb* = file:/services/usb/OWNERS
per-file com_android_server_Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS
per-file com_android_server_accessibility_* = file:/services/accessibility/OWNERS
+per-file com_android_server_display_* = file:/services/core/java/com/android/server/display/OWNERS
per-file com_android_server_hdmi_* = file:/core/java/android/hardware/hdmi/OWNERS
per-file com_android_server_lights_* = file:/services/core/java/com/android/server/lights/OWNERS
per-file com_android_server_location_* = file:/location/java/android/location/OWNERS
diff --git a/services/core/jni/com_android_server_am_OomConnection.cpp b/services/core/jni/com_android_server_am_OomConnection.cpp
index 054937f..4d07776 100644
--- a/services/core/jni/com_android_server_am_OomConnection.cpp
+++ b/services/core/jni/com_android_server_am_OomConnection.cpp
@@ -92,9 +92,11 @@
memevent_listener.deregisterAllEvents();
jniThrowRuntimeException(env, "Failed creating java string for process name");
}
- jobject java_oom_kill = env->NewObject(sOomKillRecordInfo.clazz, sOomKillRecordInfo.ctor,
- oom_kill.timestamp_ms, oom_kill.pid, oom_kill.uid,
- process_name, oom_kill.oom_score_adj);
+ jobject java_oom_kill =
+ env->NewObject(sOomKillRecordInfo.clazz, sOomKillRecordInfo.ctor,
+ oom_kill.timestamp_ms, oom_kill.pid, oom_kill.uid, process_name,
+ oom_kill.oom_score_adj, oom_kill.total_vm_kb, oom_kill.anon_rss_kb,
+ oom_kill.file_rss_kb, oom_kill.shmem_rss_kb, oom_kill.pgtables_kb);
if (java_oom_kill == NULL) {
memevent_listener.deregisterAllEvents();
jniThrowRuntimeException(env, "Failed to create OomKillRecord object");
@@ -115,8 +117,8 @@
sOomKillRecordInfo.clazz = FindClassOrDie(env, "android/os/OomKillRecord");
sOomKillRecordInfo.clazz = MakeGlobalRefOrDie(env, sOomKillRecordInfo.clazz);
- sOomKillRecordInfo.ctor =
- GetMethodIDOrDie(env, sOomKillRecordInfo.clazz, "<init>", "(JIILjava/lang/String;S)V");
+ sOomKillRecordInfo.ctor = GetMethodIDOrDie(env, sOomKillRecordInfo.clazz, "<init>",
+ "(JIILjava/lang/String;SJJJJJ)V");
return RegisterMethodsOrDie(env, "com/android/server/am/OomConnection", sOomConnectionMethods,
NELEM(sOomConnectionMethods));
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 88c47f3..2f880ba 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -128,7 +128,8 @@
jmethodID getVirtualKeyQuietTimeMillis;
jmethodID getExcludedDeviceNames;
jmethodID getInputPortAssociations;
- jmethodID getInputUniqueIdAssociations;
+ jmethodID getInputUniqueIdAssociationsByPort;
+ jmethodID getInputUniqueIdAssociationsByDescriptor;
jmethodID getDeviceTypeAssociations;
jmethodID getKeyboardLayoutAssociations;
jmethodID getHoverTapTimeout;
@@ -634,10 +635,13 @@
env->DeleteLocalRef(portAssociations);
}
- outConfig->uniqueIdAssociations =
- readMapFromInterleavedJavaArray<std::string>(gServiceClassInfo
- .getInputUniqueIdAssociations,
- "getInputUniqueIdAssociations");
+ outConfig->uniqueIdAssociationsByPort = readMapFromInterleavedJavaArray<
+ std::string>(gServiceClassInfo.getInputUniqueIdAssociationsByPort,
+ "getInputUniqueIdAssociationsByPort");
+
+ outConfig->uniqueIdAssociationsByDescriptor = readMapFromInterleavedJavaArray<
+ std::string>(gServiceClassInfo.getInputUniqueIdAssociationsByDescriptor,
+ "getInputUniqueIdAssociationsByDescriptor");
outConfig->deviceTypeAssociations =
readMapFromInterleavedJavaArray<std::string>(gServiceClassInfo
@@ -3090,8 +3094,11 @@
GET_METHOD_ID(gServiceClassInfo.getInputPortAssociations, clazz,
"getInputPortAssociations", "()[Ljava/lang/String;");
- GET_METHOD_ID(gServiceClassInfo.getInputUniqueIdAssociations, clazz,
- "getInputUniqueIdAssociations", "()[Ljava/lang/String;");
+ GET_METHOD_ID(gServiceClassInfo.getInputUniqueIdAssociationsByPort, clazz,
+ "getInputUniqueIdAssociationsByPort", "()[Ljava/lang/String;");
+
+ GET_METHOD_ID(gServiceClassInfo.getInputUniqueIdAssociationsByDescriptor, clazz,
+ "getInputUniqueIdAssociationsByDescriptor", "()[Ljava/lang/String;");
GET_METHOD_ID(gServiceClassInfo.getDeviceTypeAssociations, clazz, "getDeviceTypeAssociations",
"()[Ljava/lang/String;");
diff --git a/services/core/jni/linux/usb/f_accessory.h b/services/core/jni/linux/usb/f_accessory.h
new file mode 100644
index 0000000..abd864c
--- /dev/null
+++ b/services/core/jni/linux/usb/f_accessory.h
@@ -0,0 +1,34 @@
+/*
+ * This file is auto-generated. Modifications will be lost.
+ *
+ * See https://android.googlesource.com/platform/bionic/+/master/libc/kernel/
+ * for more information.
+ */
+#ifndef _UAPI_LINUX_USB_F_ACCESSORY_H
+#define _UAPI_LINUX_USB_F_ACCESSORY_H
+#define USB_ACCESSORY_VENDOR_ID 0x18D1
+#define USB_ACCESSORY_PRODUCT_ID 0x2D00
+#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01
+#define ACCESSORY_STRING_MANUFACTURER 0
+#define ACCESSORY_STRING_MODEL 1
+#define ACCESSORY_STRING_DESCRIPTION 2
+#define ACCESSORY_STRING_VERSION 3
+#define ACCESSORY_STRING_URI 4
+#define ACCESSORY_STRING_SERIAL 5
+#define ACCESSORY_GET_PROTOCOL 51
+#define ACCESSORY_SEND_STRING 52
+#define ACCESSORY_START 53
+#define ACCESSORY_REGISTER_HID 54
+#define ACCESSORY_UNREGISTER_HID 55
+#define ACCESSORY_SET_HID_REPORT_DESC 56
+#define ACCESSORY_SEND_HID_EVENT 57
+#define ACCESSORY_SET_AUDIO_MODE 58
+#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256])
+#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256])
+#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256])
+#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256])
+#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256])
+#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256])
+#define ACCESSORY_IS_START_REQUESTED _IO('M', 7)
+#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8)
+#endif
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index f1962cb..6143f1d 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -176,7 +176,9 @@
<xs:element type="idleScreenRefreshRateTimeout" name="idleScreenRefreshRateTimeout" minOccurs="0">
<xs:annotation name="final"/>
</xs:element>
-
+ <xs:element name="supportsVrr" type="xs:boolean" minOccurs="0">
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 170434c..45ec8f2 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -152,6 +152,7 @@
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncreaseIdle();
method public final com.android.server.display.config.SensorDetails getScreenOffBrightnessSensor();
method public final com.android.server.display.config.IntegerArray getScreenOffBrightnessSensorValueToLux();
+ method public final boolean getSupportsVrr();
method public final com.android.server.display.config.SensorDetails getTempSensor();
method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
method public final com.android.server.display.config.UsiVersion getUsiVersion();
@@ -188,6 +189,7 @@
method public final void setScreenBrightnessRampSlowIncreaseIdle(java.math.BigDecimal);
method public final void setScreenOffBrightnessSensor(com.android.server.display.config.SensorDetails);
method public final void setScreenOffBrightnessSensorValueToLux(com.android.server.display.config.IntegerArray);
+ method public final void setSupportsVrr(boolean);
method public final void setTempSensor(com.android.server.display.config.SensorDetails);
method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling);
method public final void setUsiVersion(com.android.server.display.config.UsiVersion);
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index f5ba50d..73e53e2 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -21,13 +21,13 @@
import android.content.Context;
import android.credentials.ClearCredentialStateException;
import android.credentials.ClearCredentialStateRequest;
+import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
import android.credentials.IClearCredentialStateCallback;
import android.credentials.selection.ProviderData;
import android.credentials.selection.RequestInfo;
import android.os.CancellationSignal;
import android.os.RemoteException;
-import android.os.ResultReceiver;
import android.service.credentials.CallingAppInfo;
import android.util.Slog;
@@ -41,7 +41,7 @@
public final class ClearRequestSession extends RequestSession<ClearCredentialStateRequest,
IClearCredentialStateCallback, Void>
implements ProviderSession.ProviderInternalCallback<Void> {
- private static final String TAG = "GetRequestSession";
+ private static final String TAG = CredentialManager.TAG;
public ClearRequestSession(Context context, RequestSession.SessionLifetime sessionCallback,
Object lock, int userId, int callingUid,
@@ -150,7 +150,7 @@
}
@Override
- public void onUiCancellation(boolean isUserCancellation, ResultReceiver resultReceiver) {
+ public void onUiCancellation(boolean isUserCancellation) {
// Not needed since UI is not involved
}
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index cac42b1..9781fb9 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -31,7 +31,6 @@
import android.credentials.selection.RequestInfo;
import android.os.CancellationSignal;
import android.os.RemoteException;
-import android.os.ResultReceiver;
import android.service.credentials.CallingAppInfo;
import android.service.credentials.PermissionUtils;
import android.util.Slog;
@@ -50,7 +49,7 @@
public final class CreateRequestSession extends RequestSession<CreateCredentialRequest,
ICreateCredentialCallback, CreateCredentialResponse>
implements ProviderSession.ProviderInternalCallback<CreateCredentialResponse> {
- private static final String TAG = "CreateRequestSession";
+ private static final String TAG = CredentialManager.TAG;
private final Set<ComponentName> mPrimaryProviders;
CreateRequestSession(@NonNull Context context, RequestSession.SessionLifetime sessionCallback,
@@ -164,7 +163,7 @@
}
@Override
- public void onUiCancellation(boolean isUserCancellation, ResultReceiver resultReceiver) {
+ public void onUiCancellation(boolean isUserCancellation) {
String exception = CreateCredentialException.TYPE_USER_CANCELED;
String message = "User cancelled the selector";
if (!isUserCancellation) {
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 281fb1c..6ef1436 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -34,6 +34,7 @@
import android.credentials.ClearCredentialStateRequest;
import android.credentials.CreateCredentialException;
import android.credentials.CreateCredentialRequest;
+import android.credentials.CredentialManager;
import android.credentials.CredentialOption;
import android.credentials.CredentialProviderInfo;
import android.credentials.GetCandidateCredentialsException;
@@ -92,7 +93,7 @@
extends AbstractMasterSystemService<
CredentialManagerService, CredentialManagerServiceImpl> {
- private static final String TAG = "CredManSysService";
+ private static final String TAG = CredentialManager.TAG;
private static final String PERMISSION_DENIED_ERROR = "permission_denied";
private static final String PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR =
"Caller is missing WRITE_SECURE_SETTINGS permission";
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
index 379800b..38ad5b6 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
@@ -21,6 +21,7 @@
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
import android.service.credentials.CredentialProviderInfoFactory;
import android.util.Slog;
@@ -36,7 +37,7 @@
*/
public final class CredentialManagerServiceImpl extends
AbstractPerUserSystemService<CredentialManagerServiceImpl, CredentialManagerService> {
- private static final String TAG = "CredManSysServiceImpl";
+ private static final String TAG = CredentialManager.TAG;
@GuardedBy("mLock")
@NonNull
@@ -93,7 +94,10 @@
public ProviderSession initiateProviderSessionForRequestLocked(
RequestSession requestSession, List<String> requestOptions) {
if (!requestOptions.isEmpty() && !isServiceCapableLocked(requestOptions)) {
- Slog.i(TAG, "Service does not have the required capabilities");
+ if (mInfo != null) {
+ Slog.i(TAG, "Service does not have the required capabilities: "
+ + mInfo.getComponentName());
+ }
return null;
}
if (mInfo == null) {
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 24f6697..e73dacb 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -15,8 +15,6 @@
*/
package com.android.server.credentials;
-import static android.credentials.selection.Constants.EXTRA_FINAL_RESPONSE_RECEIVER;
-
import android.annotation.NonNull;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -48,7 +46,6 @@
/** Initiates the Credential Manager UI and receives results. */
public class CredentialManagerUi {
- private static final String TAG = "CredentialManagerUi";
@NonNull
private final CredentialManagerUiCallback mCallbacks;
@NonNull
@@ -83,25 +80,18 @@
UserSelectionDialogResult selection = UserSelectionDialogResult
.fromResultData(resultData);
if (selection != null) {
- ResultReceiver resultReceiver = resultData.getParcelable(
- EXTRA_FINAL_RESPONSE_RECEIVER,
- ResultReceiver.class);
- mCallbacks.onUiSelection(selection, resultReceiver);
+ mCallbacks.onUiSelection(selection);
}
break;
case UserSelectionDialogResult.RESULT_CODE_DIALOG_USER_CANCELED:
mStatus = UiStatus.TERMINATED;
- mCallbacks.onUiCancellation(/* isUserCancellation= */ true,
- resultData.getParcelable(EXTRA_FINAL_RESPONSE_RECEIVER,
- ResultReceiver.class));
+ mCallbacks.onUiCancellation(/* isUserCancellation= */ true);
break;
case UserSelectionDialogResult.RESULT_CODE_CANCELED_AND_LAUNCHED_SETTINGS:
mStatus = UiStatus.TERMINATED;
- mCallbacks.onUiCancellation(/* isUserCancellation= */ false,
- resultData.getParcelable(EXTRA_FINAL_RESPONSE_RECEIVER,
- ResultReceiver.class));
+ mCallbacks.onUiCancellation(/* isUserCancellation= */ false);
break;
case UserSelectionDialogResult.RESULT_CODE_DATA_PARSING_FAILURE:
mStatus = UiStatus.TERMINATED;
@@ -125,10 +115,10 @@
*/
public interface CredentialManagerUiCallback {
/** Called when the user makes a selection. */
- void onUiSelection(UserSelectionDialogResult selection, ResultReceiver resultReceiver);
+ void onUiSelection(UserSelectionDialogResult selection);
/** Called when the UI is canceled without a successful provider result. */
- void onUiCancellation(boolean isUserCancellation, ResultReceiver resultReceiver);
+ void onUiCancellation(boolean isUserCancellation);
/** Called when the selector UI fails to come up (mostly due to parsing issue today). */
void onUiSelectorInvocationFailure();
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index fd2a9a2..b1673e2 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -22,9 +22,11 @@
import android.content.Context;
import android.content.Intent;
import android.credentials.Constants;
+import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
import android.credentials.GetCandidateCredentialsException;
import android.credentials.GetCandidateCredentialsResponse;
+import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.GetCredentialResponse;
import android.credentials.IGetCandidateCredentialsCallback;
@@ -54,7 +56,7 @@
public class GetCandidateRequestSession extends RequestSession<GetCredentialRequest,
IGetCandidateCredentialsCallback, GetCandidateCredentialsResponse>
implements ProviderSession.ProviderInternalCallback<GetCredentialResponse> {
- private static final String TAG = "GetCandidateRequestSession";
+ private static final String TAG = CredentialManager.TAG;
private static final String SESSION_ID_KEY = "autofill_session_id";
private static final String REQUEST_ID_KEY = "autofill_request_id";
@@ -63,6 +65,8 @@
private final int mAutofillSessionId;
private final int mAutofillRequestId;
+ private final ResultReceiver mAutofillCallback;
+
public GetCandidateRequestSession(
Context context, SessionLifetime sessionCallback,
Object lock, int userId, int callingUid,
@@ -76,6 +80,8 @@
mClientBinder = clientBinder;
mAutofillSessionId = request.getData().getInt(SESSION_ID_KEY, -1);
mAutofillRequestId = request.getData().getInt(REQUEST_ID_KEY, -1);
+ mAutofillCallback = request.getData().getParcelable(
+ CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, ResultReceiver.class);
if (mClientBinder != null) {
setUpClientCallbackListener(mClientBinder);
}
@@ -154,34 +160,34 @@
public void onFinalErrorReceived(ComponentName componentName, String errorType,
String message) {
Slog.d(TAG, "onFinalErrorReceived");
- respondToFinalReceiverWithFailureAndFinish(this.mFinalResponseReceiver, errorType, message);
+ if (GetCredentialException.TYPE_USER_CANCELED.equals(errorType)) {
+ Slog.d(TAG, "User canceled but session is not being terminated");
+ return;
+ }
+ respondToFinalReceiverWithFailureAndFinish(errorType, message);
}
@Override
- public void onUiCancellation(boolean isUserCancellation,
- @Nullable ResultReceiver finalResponseReceiver) {
- String exception = GetCandidateCredentialsException.TYPE_USER_CANCELED;
- String message = "User cancelled the selector";
- if (!isUserCancellation) {
- exception = GetCandidateCredentialsException.TYPE_INTERRUPTED;
- message = "The UI was interrupted - please try again.";
- }
- mRequestSessionMetric.collectFrameworkException(exception);
- respondToFinalReceiverWithFailureAndFinish(finalResponseReceiver, exception, message);
+ public void onUiCancellation(boolean isUserCancellation) {
+ Slog.d(TAG, "User canceled but session is not being terminated");
}
private void respondToFinalReceiverWithFailureAndFinish(
- ResultReceiver finalResponseReceiver,
String exception, String message
) {
- if (finalResponseReceiver != null) {
+ if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) {
+ Slog.w(TAG, "Request has already been completed. This is strange.");
+ return;
+ }
+
+ if (mAutofillCallback != null) {
Bundle resultData = new Bundle();
resultData.putStringArray(
CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
new String[] {exception, message});
- finalResponseReceiver.send(Constants.FAILURE_CREDMAN_SELECTOR, resultData);
+ mAutofillCallback.send(Constants.FAILURE_CREDMAN_SELECTOR, resultData);
} else {
- Slog.w(TAG, "onUiCancellation called but finalResponseReceiver not found");
+ Slog.w(TAG, "onUiCancellation called but mAutofillCallback not found");
}
finishSession(/*propagateCancellation=*/false, ApiStatus.FAILURE.getMetricCode());
}
@@ -218,12 +224,25 @@
public void onFinalResponseReceived(ComponentName componentName,
GetCredentialResponse response) {
Slog.d(TAG, "onFinalResponseReceived");
- if (this.mFinalResponseReceiver != null) {
+ if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) {
+ Slog.w(TAG, "Request has already been completed. This is strange.");
+ return;
+ }
+ respondToFinalReceiverWithResponseAndFinish(response);
+ }
+
+ private void respondToFinalReceiverWithResponseAndFinish(GetCredentialResponse response) {
+ if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) {
+ Slog.w(TAG, "Request has already been completed. This is strange.");
+ return;
+ }
+
+ if (this.mAutofillCallback != null) {
Slog.d(TAG, "onFinalResponseReceived sending through final receiver");
Bundle resultData = new Bundle();
resultData.putParcelable(
CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, response);
- mFinalResponseReceiver.send(Constants.SUCCESS_CREDMAN_SELECTOR, resultData);
+ mAutofillCallback.send(Constants.SUCCESS_CREDMAN_SELECTOR, resultData);
finishSession(/*propagateCancellation=*/ false, ApiStatus.SUCCESS.getMetricCode());
} else {
Slog.w(TAG, "onFinalResponseReceived result receiver not found for pinned entry");
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index d55d8ef..be36b6c 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.credentials.CredentialManager;
import android.credentials.CredentialOption;
import android.credentials.CredentialProviderInfo;
import android.credentials.GetCredentialException;
@@ -31,7 +32,6 @@
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.RemoteException;
-import android.os.ResultReceiver;
import android.service.credentials.CallingAppInfo;
import android.service.credentials.PermissionUtils;
import android.util.Slog;
@@ -48,7 +48,7 @@
public class GetRequestSession extends RequestSession<GetCredentialRequest,
IGetCredentialCallback, GetCredentialResponse>
implements ProviderSession.ProviderInternalCallback<GetCredentialResponse> {
- private static final String TAG = "GetRequestSession";
+ private static final String TAG = CredentialManager.TAG;
public GetRequestSession(Context context, RequestSession.SessionLifetime sessionCallback,
Object lock, int userId, int callingUid,
@@ -165,7 +165,7 @@
}
@Override
- public void onUiCancellation(boolean isUserCancellation, ResultReceiver resultReceiver) {
+ public void onUiCancellation(boolean isUserCancellation) {
String exception = GetCredentialException.TYPE_USER_CANCELED;
String message = "User cancelled the selector";
if (!isUserCancellation) {
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index 16bf1778..ac4aac6 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -20,6 +20,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.credentials.CredentialManager;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
@@ -44,7 +45,7 @@
public class MetricUtilities {
private static final boolean LOG_FLAG = true;
- private static final String TAG = "MetricUtilities";
+ private static final String TAG = CredentialManager.TAG;
public static final String USER_CANCELED_SUBSTRING = "TYPE_USER_CANCELED";
public static final int MIN_EMIT_WAIT_TIME_MS = 10;
diff --git a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
index e4b5c77..f6b107b 100644
--- a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
@@ -21,6 +21,7 @@
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
+import android.credentials.CredentialManager;
import android.credentials.CredentialOption;
import android.credentials.GetCredentialRequest;
import android.credentials.IGetCredentialCallback;
@@ -44,7 +45,7 @@
* responses from providers, and the UX app, and updates the provider(s) state.
*/
public class PrepareGetRequestSession extends GetRequestSession {
- private static final String TAG = "PrepareGetRequestSession";
+ private static final String TAG = CredentialManager.TAG;
private final IPrepareGetCredentialCallback mPrepareGetCredentialCallback;
diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
index 6a1b1db7..6759dbb 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
@@ -20,6 +20,7 @@
import android.annotation.UserIdInt;
import android.content.Context;
import android.credentials.ClearCredentialStateException;
+import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
import android.credentials.selection.ProviderData;
import android.credentials.selection.ProviderPendingIntentResponse;
@@ -37,7 +38,7 @@
Void>
implements
RemoteCredentialService.ProviderCallbacks<Void> {
- private static final String TAG = "ProviderClearSession";
+ private static final String TAG = CredentialManager.TAG;
private ClearCredentialStateException mProviderException;
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index 6361aeb..bee7f6c 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.credentials.CreateCredentialException;
import android.credentials.CreateCredentialResponse;
+import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
import android.credentials.selection.CreateCredentialProviderData;
import android.credentials.selection.Entry;
@@ -51,7 +52,7 @@
*/
public final class ProviderCreateSession extends ProviderSession<
BeginCreateCredentialRequest, BeginCreateCredentialResponse> {
- private static final String TAG = "ProviderCreateSession";
+ private static final String TAG = CredentialManager.TAG;
// Key to be used as an entry key for a save entry
public static final String SAVE_ENTRY_KEY = "save_entry_key";
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index c5f2921..e18ef2b 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -22,6 +22,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.credentials.CredentialManager;
import android.credentials.CredentialOption;
import android.credentials.CredentialProviderInfo;
import android.credentials.GetCredentialException;
@@ -61,7 +62,7 @@
BeginGetCredentialResponse>
implements
RemoteCredentialService.ProviderCallbacks<BeginGetCredentialResponse> {
- private static final String TAG = "ProviderGetSession";
+ private static final String TAG = CredentialManager.TAG;
// Key to be used as the entry key for an action entry
public static final String ACTION_ENTRY_KEY = "action_key";
// Key to be used as the entry key for the authentication entry
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
index f162916..83f9c24 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -22,6 +22,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.credentials.CredentialManager;
import android.credentials.CredentialOption;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialResponse;
@@ -58,7 +59,7 @@
public class ProviderRegistryGetSession extends ProviderSession<CredentialOption,
Set<CredentialDescriptionRegistry.FilterResult>> {
- private static final String TAG = "ProviderRegistryGetSession";
+ private static final String TAG = CredentialManager.TAG;
@VisibleForTesting
static final String CREDENTIAL_ENTRY_KEY = "credential_key";
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index dfc08f0..8f0ae90 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -24,6 +24,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.credentials.Credential;
+import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
import android.credentials.selection.ProviderData;
import android.credentials.selection.ProviderPendingIntentResponse;
@@ -44,7 +45,7 @@
public abstract class ProviderSession<T, R>
implements RemoteCredentialService.ProviderCallbacks<R> {
- private static final String TAG = "ProviderSession";
+ private static final String TAG = CredentialManager.TAG;
@NonNull
protected final Context mContext;
diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
index 4bcf8be..c361406 100644
--- a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
+++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.credentials.ClearCredentialStateException;
import android.credentials.CreateCredentialException;
+import android.credentials.CredentialManager;
import android.credentials.GetCredentialException;
import android.os.Binder;
import android.os.Handler;
@@ -58,7 +59,7 @@
*/
public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialProviderService> {
- private static final String TAG = "RemoteCredentialService";
+ private static final String TAG = CredentialManager.TAG;
/** Timeout for a single request. */
private static final long TIMEOUT_REQUEST_MILLIS = 3 * DateUtils.SECOND_IN_MILLIS;
/** Timeout to unbind after the task queue is empty. */
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index a5b9aa6..c0bc8e0 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -17,12 +17,12 @@
package com.android.server.credentials;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
import android.credentials.flags.Flags;
import android.credentials.selection.ProviderData;
@@ -34,7 +34,6 @@
import android.os.IInterface;
import android.os.Looper;
import android.os.RemoteException;
-import android.os.ResultReceiver;
import android.os.UserHandle;
import android.service.credentials.CallingAppInfo;
import android.util.Slog;
@@ -56,7 +55,7 @@
* every time a new response type is expected from the providers.
*/
abstract class RequestSession<T, U, V> implements CredentialManagerUi.CredentialManagerUiCallback {
- private static final String TAG = "RequestSession";
+ private static final String TAG = CredentialManager.TAG;
public interface SessionLifetime {
/** Called when the user makes a selection. */
@@ -103,9 +102,6 @@
protected PendingIntent mPendingIntent;
- @Nullable
- protected ResultReceiver mFinalResponseReceiver;
-
@NonNull
protected RequestSessionStatus mRequestSessionStatus =
RequestSessionStatus.IN_PROGRESS;
@@ -224,8 +220,7 @@
// UI callbacks
@Override // from CredentialManagerUiCallbacks
- public void onUiSelection(UserSelectionDialogResult selection,
- ResultReceiver finalResponseReceiver) {
+ public void onUiSelection(UserSelectionDialogResult selection) {
if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) {
Slog.w(TAG, "Request has already been completed. This is strange.");
return;
@@ -241,7 +236,7 @@
Slog.w(TAG, "providerSession not found in onUiSelection. This is strange.");
return;
}
- mFinalResponseReceiver = finalResponseReceiver;
+
ProviderSessionMetric providerSessionMetric = providerSession.mProviderSessionMetric;
int initialAuthMetricsProvider = providerSessionMetric.getBrowsedAuthenticationMetric()
.size();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index f39d019..065c14e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -103,7 +103,7 @@
UserManager.DISALLOW_CELLULAR_2G);
//TODO(b/295504706) : Speak to security team to decide what to set Policy_Size_Limit
- private static final int DEFAULT_POLICY_SIZE_LIMIT = -1;
+ static final int DEFAULT_POLICY_SIZE_LIMIT = -1;
private final Context mContext;
private final UserManager mUserManager;
@@ -225,7 +225,7 @@
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
policyDefinition, userId)) {
return;
@@ -350,7 +350,7 @@
}
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
}
@@ -496,7 +496,7 @@
synchronized (mLock) {
PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
policyDefinition, UserHandle.USER_ALL)) {
return;
@@ -568,7 +568,7 @@
synchronized (mLock) {
PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
decreasePolicySizeForAdmin(policyState, enforcingAdmin);
}
@@ -1598,6 +1598,7 @@
existingPolicySize = sizeOf(policyState.getPoliciesSetByAdmins().get(admin));
}
int policySize = sizeOf(value);
+
// Policy size limit is disabled if mPolicySizeLimit is -1.
if (mPolicySizeLimit == -1
|| currentAdminPoliciesSize + policySize - existingPolicySize < mPolicySizeLimit) {
@@ -1657,10 +1658,6 @@
* the limitation.
*/
void setMaxPolicyStorageLimit(int storageLimit) {
- if (storageLimit < DEFAULT_POLICY_SIZE_LIMIT && storageLimit != -1) {
- throw new IllegalArgumentException("Can't set a size limit less than the minimum "
- + "allowed size.");
- }
mPolicySizeLimit = storageLimit;
}
@@ -1672,6 +1669,15 @@
return mPolicySizeLimit;
}
+ int getPolicySizeForAdmin(EnforcingAdmin admin) {
+ if (mAdminPolicySize.contains(admin.getUserId())
+ && mAdminPolicySize.get(
+ admin.getUserId()).containsKey(admin)) {
+ return mAdminPolicySize.get(admin.getUserId()).get(admin);
+ }
+ return 0;
+ }
+
public void dump(IndentingPrintWriter pw) {
synchronized (mLock) {
pw.println("Local Policies: ");
@@ -1906,7 +1912,7 @@
private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
if (mAdminPolicySize != null) {
for (int i = 0; i < mAdminPolicySize.size(); i++) {
int userId = mAdminPolicySize.keyAt(i);
@@ -1930,7 +1936,7 @@
private void writeMaxPolicySizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
return;
}
serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT);
@@ -2095,7 +2101,7 @@
private void readMaxPolicySizeInner(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
return;
}
mPolicySizeLimit = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1dd719e..e1ad979d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -88,6 +88,7 @@
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIFI;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WINDOWS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIPE_DATA;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT;
import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
import static android.Manifest.permission.MASTER_CLEAR;
import static android.Manifest.permission.NOTIFY_PENDING_SYSTEM_UPDATE;
@@ -106,6 +107,8 @@
import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION;
import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS;
import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_SUSPENSION;
+import static android.app.AppOpsManager.OP_RUN_ANY_IN_BACKGROUND;
+import static android.app.AppOpsManager.OP_RUN_IN_BACKGROUND;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
@@ -268,6 +271,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
+import static com.android.server.devicepolicy.DevicePolicyEngine.DEFAULT_POLICY_SIZE_LIMIT;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -742,7 +746,8 @@
* These cannot be set on the managed profile's parent DPM instance
*/
private static final int PROFILE_KEYGUARD_FEATURES_PROFILE_ONLY =
- DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+ DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS
+ | DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL;
/** Keyguard features that are allowed to be set on a managed profile */
private static final int PROFILE_KEYGUARD_FEATURES =
@@ -883,10 +888,6 @@
"enable_permission_based_access";
private static final boolean DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG = false;
- // TODO(b/266831522) remove the flag after rollout.
- private static final String APPLICATION_EXEMPTIONS_FLAG = "application_exemptions";
- private static final boolean DEFAULT_APPLICATION_EXEMPTIONS_FLAG = true;
-
private static final int RETRY_COPY_ACCOUNT_ATTEMPTS = 3;
/**
@@ -2251,11 +2252,41 @@
if (userHandle == UserHandle.USER_SYSTEM) {
mStateCache.setDeviceProvisioned(policy.mUserSetupComplete);
}
+ if (Flags.headlessSingleUserBadDeviceAdminStateFix()) {
+ fixBadDeviceAdminStateForInternalUsers(userHandle, policy);
+ }
}
return policy;
}
}
+ private void fixBadDeviceAdminStateForInternalUsers(int userId, DevicePolicyData policy) {
+ ComponentName component = mOwners.getDeviceOwnerComponent();
+ int doUserId = mOwners.getDeviceOwnerUserId();
+ ComponentName cloudDpc = new ComponentName(
+ "com.google.android.apps.work.clouddpc",
+ "com.google.android.apps.work.clouddpc.receivers.CloudDeviceAdminReceiver");
+ if (component == null || doUserId != userId || !component.equals(cloudDpc)) {
+ return;
+ }
+ Slogf.i(LOG_TAG, "Attempting to apply a temp fix for cloudpc internal users' bad state.");
+ final int n = policy.mAdminList.size();
+ for (int i = 0; i < n; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (component.equals(admin.info.getComponent())) {
+ Slogf.i(LOG_TAG, "An ActiveAdmin already exists, fix not required.");
+ return;
+ }
+ }
+ DeviceAdminInfo dai = findAdmin(component, userId, /* throwForMissingPermission= */ false);
+ if (dai != null) {
+ ActiveAdmin ap = new ActiveAdmin(dai, /* parent */ false);
+ policy.mAdminMap.put(ap.info.getComponent(), ap);
+ policy.mAdminList.add(ap);
+ Slogf.i(LOG_TAG, "Fix applied, an ActiveAdmin has been added.");
+ }
+ }
+
/**
* Creates and loads the policy data from xml for data that is shared between
* various profiles of a user. In contrast to {@link #getUserData(int)}
@@ -3656,26 +3687,6 @@
mDevicePolicyEngine.handleStartUser(userId);
}
- void pushUserControlDisabledPackagesLocked(int userId) {
- final int targetUserId;
- final ActiveAdmin owner;
- if (getDeviceOwnerUserIdUncheckedLocked() == userId) {
- owner = getDeviceOwnerAdminLocked();
- targetUserId = UserHandle.USER_ALL;
- } else {
- owner = getProfileOwnerAdminLocked(userId);
- targetUserId = userId;
- }
-
- List<String> protectedPackages = (owner == null || owner.protectedPackages == null)
- ? null : owner.protectedPackages;
- mInjector.binderWithCleanCallingIdentity(() ->
- mInjector.getPackageManagerInternal().setOwnerProtectedPackages(
- targetUserId, protectedPackages));
- mUsageStatsManagerInternal.setAdminProtectedPackages(new ArraySet(protectedPackages),
- targetUserId);
- }
-
void handleUnlockUser(int userId) {
startOwnerService(userId, "unlock-user");
mDevicePolicyEngine.handleUnlockUser(userId);
@@ -12138,7 +12149,7 @@
}
if (packageList != null) {
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
for (String pkg : packageList) {
PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
}
@@ -13913,7 +13924,7 @@
return;
}
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(accountType, "account type");
}
@@ -14527,7 +14538,7 @@
public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)
throws SecurityException {
Objects.requireNonNull(packages, "packages is null");
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
for (String pkg : packages) {
PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
}
@@ -15880,14 +15891,6 @@
}
@Override
- public boolean isApplicationExemptionsFlagEnabled() {
- return DeviceConfig.getBoolean(
- NAMESPACE_DEVICE_POLICY_MANAGER,
- APPLICATION_EXEMPTIONS_FLAG,
- DEFAULT_APPLICATION_EXEMPTIONS_FLAG);
- }
-
- @Override
public List<Bundle> getApplicationRestrictionsPerAdminForUser(
String packageName, @UserIdInt int userId) {
if (UserHandle.getCallingUserId() != userId
@@ -20345,34 +20348,47 @@
hasCallingOrSelfPermission(permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS));
final CallerIdentity caller = getCallerIdentity(callerPackage);
- final ApplicationInfo packageInfo;
- packageInfo = getPackageInfoWithNullCheck(packageName, caller);
+ final AppOpsManager appOpsMgr = mInjector.getAppOpsManager();
+ final ApplicationInfo appInfo = getPackageInfoWithNullCheck(packageName, caller);
+ final int uid = appInfo.uid;
- for (Map.Entry<Integer, String> entry :
- APPLICATION_EXEMPTION_CONSTANTS_TO_APP_OPS.entrySet()) {
- int currentMode = mInjector.getAppOpsManager().unsafeCheckOpNoThrow(
- entry.getValue(), packageInfo.uid, packageInfo.packageName);
- int newMode = ArrayUtils.contains(exemptions, entry.getKey())
- ? MODE_ALLOWED : MODE_DEFAULT;
- mInjector.binderWithCleanCallingIdentity(() -> {
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ APPLICATION_EXEMPTION_CONSTANTS_TO_APP_OPS.forEach((exemption, appOp) -> {
+ int currentMode = appOpsMgr.unsafeCheckOpNoThrow(appOp, uid, packageName);
+ int newMode = ArrayUtils.contains(exemptions, exemption)
+ ? MODE_ALLOWED : MODE_DEFAULT;
if (currentMode != newMode) {
- mInjector.getAppOpsManager()
- .setMode(entry.getValue(),
- packageInfo.uid,
- packageName,
- newMode);
+ appOpsMgr.setMode(appOp, uid, packageName, newMode);
+
+ // If the user has already disabled background usage for the package, it won't
+ // have OP_RUN_ANY_IN_BACKGROUND app op and won't execute in the background. The
+ // code below grants that app op, and once the exemption is in place, the user
+ // won't be able to disable background usage anymore.
+ if (Flags.powerExemptionBgUsageFix()
+ && exemption == EXEMPT_FROM_POWER_RESTRICTIONS
+ && newMode == MODE_ALLOWED) {
+ setBgUsageAppOp(appOpsMgr, appInfo);
+ }
}
});
- }
+ });
+
String[] appOpExemptions = new String[exemptions.length];
for (int i = 0; i < exemptions.length; i++) {
appOpExemptions[i] = APPLICATION_EXEMPTION_CONSTANTS_TO_APP_OPS.get(exemptions[i]);
}
DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.SET_APPLICATION_EXEMPTIONS)
- .setAdmin(caller.getPackageName())
- .setStrings(packageName, appOpExemptions)
- .write();
+ .createEvent(DevicePolicyEnums.SET_APPLICATION_EXEMPTIONS)
+ .setAdmin(caller.getPackageName())
+ .setStrings(packageName, appOpExemptions)
+ .write();
+ }
+
+ static void setBgUsageAppOp(AppOpsManager appOpsMgr, ApplicationInfo appInfo) {
+ appOpsMgr.setMode(OP_RUN_ANY_IN_BACKGROUND, appInfo.uid, appInfo.packageName, MODE_ALLOWED);
+ if (appInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+ appOpsMgr.setMode(OP_RUN_IN_BACKGROUND, appInfo.uid, appInfo.packageName, MODE_ALLOWED);
+ }
}
@Override
@@ -24536,19 +24552,23 @@
@Override
public void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
return;
}
CallerIdentity caller = getCallerIdentity(callerPackageName);
enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(),
caller.getUserId());
+ if (storageLimit < DEFAULT_POLICY_SIZE_LIMIT && storageLimit != -1) {
+ throw new IllegalArgumentException("Can't set a size limit less than the minimum "
+ + "allowed size.");
+ }
mDevicePolicyEngine.setMaxPolicyStorageLimit(storageLimit);
}
@Override
public int getMaxPolicyStorageLimit(String callerPackageName) {
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
return -1;
}
CallerIdentity caller = getCallerIdentity(callerPackageName);
@@ -24559,6 +24579,32 @@
}
@Override
+ public void forceSetMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ return;
+ }
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ enforcePermission(MANAGE_DEVICE_POLICY_STORAGE_LIMIT, caller.getPackageName(),
+ caller.getUserId());
+
+ mDevicePolicyEngine.setMaxPolicyStorageLimit(storageLimit);
+ }
+
+ @Override
+ public int getPolicySizeForAdmin(
+ String callerPackageName, android.app.admin.EnforcingAdmin admin) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ return -1;
+ }
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ enforcePermission(MANAGE_DEVICE_POLICY_STORAGE_LIMIT, caller.getPackageName(),
+ caller.getUserId());
+
+ return mDevicePolicyEngine.getPolicySizeForAdmin(
+ EnforcingAdmin.createEnforcingAdmin(admin));
+ }
+
+ @Override
public int getHeadlessDeviceOwnerMode(String callerPackageName) {
final CallerIdentity caller = getCallerIdentity(callerPackageName);
enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(),
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
index d234dee..02590f9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
@@ -21,6 +21,7 @@
import android.app.admin.Authority;
import android.app.admin.DeviceAdminAuthority;
import android.app.admin.DpcAuthority;
+import android.app.admin.PackagePermissionPolicyKey;
import android.app.admin.RoleAuthority;
import android.app.admin.UnknownAuthority;
import android.content.ComponentName;
@@ -105,6 +106,32 @@
userId, activeAdmin);
}
+ static EnforcingAdmin createEnforcingAdmin(android.app.admin.EnforcingAdmin admin) {
+ Objects.requireNonNull(admin);
+ Authority authority = admin.getAuthority();
+ Set<String> internalAuthorities = new HashSet<>();
+ if (DpcAuthority.DPC_AUTHORITY.equals(authority)) {
+ return new EnforcingAdmin(
+ admin.getPackageName(), admin.getComponentName(),
+ Set.of(DPC_AUTHORITY), admin.getUserHandle().getIdentifier(),
+ /* activeAdmin = */ null);
+ } else if (DeviceAdminAuthority.DEVICE_ADMIN_AUTHORITY.equals(authority)) {
+ return new EnforcingAdmin(
+ admin.getPackageName(), admin.getComponentName(),
+ Set.of(DEVICE_ADMIN_AUTHORITY), admin.getUserHandle().getIdentifier(),
+ /* activeAdmin = */ null);
+ } else if (authority instanceof RoleAuthority roleAuthority) {
+ return new EnforcingAdmin(
+ admin.getPackageName(), admin.getComponentName(),
+ Set.of(DEVICE_ADMIN_AUTHORITY), admin.getUserHandle().getIdentifier(),
+ /* activeAdmin = */ null,
+ /* isRoleAuthority = */ true);
+ }
+ return new EnforcingAdmin(admin.getPackageName(), admin.getComponentName(),
+ Set.of(), admin.getUserHandle().getIdentifier(),
+ /* activeAdmin = */ null);
+ }
+
static String getRoleAuthorityOf(String roleName) {
return ROLE_AUTHORITY_PREFIX + roleName;
}
@@ -154,6 +181,20 @@
mActiveAdmin = activeAdmin;
}
+ private EnforcingAdmin(
+ String packageName, @Nullable ComponentName componentName, Set<String> authorities,
+ int userId, @Nullable ActiveAdmin activeAdmin, boolean isRoleAuthority) {
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(authorities);
+
+ mIsRoleAuthority = isRoleAuthority;
+ mPackageName = packageName;
+ mComponentName = componentName;
+ mAuthorities = new HashSet<>(authorities);
+ mUserId = userId;
+ mActiveAdmin = activeAdmin;
+ }
+
private static Set<String> getRoleAuthoritiesOrDefault(String packageName, int userId) {
Set<String> roles = getRoles(packageName, userId);
Set<String> authorities = new HashSet<>();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
index 94c1374..d1830126 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
@@ -215,6 +215,9 @@
} catch (PackageManager.NameNotFoundException e) {
return false;
}
+ if (packageInfo.applicationInfo == null || packageInfo.applicationInfo.metaData == null) {
+ return false;
+ }
final String metadataKey = sActionToMetadataKeyMap.get(provisioningAction);
return packageInfo.applicationInfo.metaData.getBoolean(metadataKey);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index 42ac998..d02cfee 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -357,7 +357,8 @@
@Override
boolean shouldWrite() {
- return (mDeviceOwner != null) || (mSystemUpdatePolicy != null)
+ return Flags.alwaysPersistDo()
+ || (mDeviceOwner != null) || (mSystemUpdatePolicy != null)
|| (mSystemUpdateInfo != null);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index e713a82..7a9fa0f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -163,8 +163,7 @@
new NoArgsPolicyKey(
DevicePolicyIdentifiers.USER_CONTROL_DISABLED_PACKAGES_POLICY),
new StringSetUnion(),
- (Set<String> value, Context context, Integer userId, PolicyKey policyKey) ->
- PolicyEnforcerCallbacks.setUserControlDisabledPackages(value, userId),
+ PolicyEnforcerCallbacks::setUserControlDisabledPackages,
new StringSetPolicySerializer());
// This is saved in the static map sPolicyDefinitions so that we're able to reconstruct the
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index a7adc5b..a0d9be54 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
@@ -29,6 +30,7 @@
import android.app.admin.PackagePolicyKey;
import android.app.admin.PolicyKey;
import android.app.admin.UserRestrictionPolicyKey;
+import android.app.admin.flags.Flags;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.Context;
@@ -37,6 +39,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -183,15 +186,31 @@
}
static boolean setUserControlDisabledPackages(
- @Nullable Set<String> packages, int userId) {
+ @Nullable Set<String> packages, Context context, int userId, PolicyKey policyKey) {
Binder.withCleanCallingIdentity(() -> {
- LocalServices.getService(PackageManagerInternal.class)
- .setOwnerProtectedPackages(
- userId,
- packages == null ? null : packages.stream().toList());
+ PackageManagerInternal pmi =
+ LocalServices.getService(PackageManagerInternal.class);
+ pmi.setOwnerProtectedPackages(userId,
+ packages == null ? null : packages.stream().toList());
LocalServices.getService(UsageStatsManagerInternal.class)
- .setAdminProtectedPackages(
+ .setAdminProtectedPackages(
packages == null ? null : new ArraySet<>(packages), userId);
+
+ if (Flags.disallowUserControlBgUsageFix()) {
+ if (packages == null) {
+ return;
+ }
+ final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ for (var pkg : packages) {
+ final var appInfo = pmi.getApplicationInfo(pkg,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ Process.myUid(), userId);
+ if (appInfo != null) {
+ DevicePolicyManagerService.setBgUsageAppOp(appOpsManager, appInfo);
+ }
+ }
+ }
});
return true;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3c6b500..648b810 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -313,8 +313,8 @@
private static final long SLOW_DELIVERY_THRESHOLD_MS = 200;
/*
- * Implementation class names. TODO: Move them to a codegen class or load
- * them from the build system somehow.
+ * Implementation class names for services in the {@code SYSTEMSERVERCLASSPATH}
+ * from {@code PRODUCT_SYSTEM_SERVER_JARS} that are *not* in {@code services.jar}.
*/
private static final String ARC_NETWORK_SERVICE_CLASS =
"com.android.server.arc.net.ArcNetworkService";
@@ -322,26 +322,6 @@
"com.android.server.arc.persistent_data_block.ArcPersistentDataBlockService";
private static final String ARC_SYSTEM_HEALTH_SERVICE =
"com.android.server.arc.health.ArcSystemHealthService";
- private static final String STATS_COMPANION_APEX_PATH =
- "/apex/com.android.os.statsd/javalib/service-statsd.jar";
- private static final String STATS_COMPANION_LIFECYCLE_CLASS =
- "com.android.server.stats.StatsCompanion$Lifecycle";
- private static final String SCHEDULING_APEX_PATH =
- "/apex/com.android.scheduling/javalib/service-scheduling.jar";
- private static final String REBOOT_READINESS_LIFECYCLE_CLASS =
- "com.android.server.scheduling.RebootReadinessManagerService$Lifecycle";
- private static final String WIFI_APEX_SERVICE_JAR_PATH =
- "/apex/com.android.wifi/javalib/service-wifi.jar";
- private static final String WIFI_SERVICE_CLASS =
- "com.android.server.wifi.WifiService";
- private static final String WIFI_SCANNING_SERVICE_CLASS =
- "com.android.server.wifi.scanner.WifiScanningService";
- private static final String WIFI_RTT_SERVICE_CLASS =
- "com.android.server.wifi.rtt.RttService";
- private static final String WIFI_AWARE_SERVICE_CLASS =
- "com.android.server.wifi.aware.WifiAwareService";
- private static final String WIFI_P2P_SERVICE_CLASS =
- "com.android.server.wifi.p2p.WifiP2pService";
private static final String LOWPAN_SERVICE_CLASS =
"com.android.server.lowpan.LowpanService";
private static final String THERMAL_OBSERVER_CLASS =
@@ -368,21 +348,19 @@
"com.android.clockwork.settings.WearSettingsService";
private static final String WRIST_ORIENTATION_SERVICE_CLASS =
"com.android.clockwork.wristorientation.WristOrientationService";
-
private static final String IOT_SERVICE_CLASS =
"com.android.things.server.IoTSystemService";
private static final String CAR_SERVICE_HELPER_SERVICE_CLASS =
"com.android.internal.car.CarServiceHelperService";
+
+ /*
+ * Implementation class names for services in the {@code SYSTEMSERVERCLASSPATH}
+ * from {@code PRODUCT_APEX_SYSTEM_SERVER_JARS}.
+ */
private static final String APPSEARCH_MODULE_LIFECYCLE_CLASS =
"com.android.server.appsearch.AppSearchModule$Lifecycle";
private static final String ISOLATED_COMPILATION_SERVICE_CLASS =
"com.android.server.compos.IsolatedCompilationService";
- private static final String CONNECTIVITY_SERVICE_APEX_PATH =
- "/apex/com.android.tethering/javalib/service-connectivity.jar";
- private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS =
- "com.android.server.ConnectivityServiceInitializer";
- private static final String NETWORK_STATS_SERVICE_INITIALIZER_CLASS =
- "com.android.server.NetworkStatsServiceInitializer";
private static final String MEDIA_COMMUNICATION_SERVICE_CLASS =
"com.android.server.media.MediaCommunicationService";
private static final String HEALTHCONNECT_MANAGER_SERVICE_CLASS =
@@ -390,17 +368,8 @@
private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService";
private static final String ENHANCED_CONFIRMATION_SERVICE_CLASS =
"com.android.ecm.EnhancedConfirmationService";
-
- private static final String UWB_APEX_SERVICE_JAR_PATH =
- "/apex/com.android.uwb/javalib/service-uwb.jar";
- private static final String UWB_SERVICE_CLASS = "com.android.server.uwb.UwbService";
- private static final String BLUETOOTH_APEX_SERVICE_JAR_PATH =
- "/apex/com.android.btservices/javalib/service-bluetooth.jar";
- private static final String BLUETOOTH_SERVICE_CLASS =
- "com.android.server.bluetooth.BluetoothService";
private static final String SAFETY_CENTER_SERVICE_CLASS =
"com.android.safetycenter.SafetyCenterService";
-
private static final String SDK_SANDBOX_MANAGER_SERVICE_CLASS =
"com.android.server.sdksandbox.SdkSandboxManagerService$Lifecycle";
private static final String AD_SERVICES_MANAGER_SERVICE_CLASS =
@@ -411,11 +380,47 @@
private static final String UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS =
"com.android.server.deviceconfig.DeviceConfigInit$Lifecycle";
+ /*
+ * Implementation class names and jar locations for services in
+ * {@code STANDALONE_SYSTEMSERVER_JARS}.
+ */
+ private static final String STATS_COMPANION_APEX_PATH =
+ "/apex/com.android.os.statsd/javalib/service-statsd.jar";
+ private static final String STATS_COMPANION_LIFECYCLE_CLASS =
+ "com.android.server.stats.StatsCompanion$Lifecycle";
+ private static final String SCHEDULING_APEX_PATH =
+ "/apex/com.android.scheduling/javalib/service-scheduling.jar";
+ private static final String REBOOT_READINESS_LIFECYCLE_CLASS =
+ "com.android.server.scheduling.RebootReadinessManagerService$Lifecycle";
+ private static final String WIFI_APEX_SERVICE_JAR_PATH =
+ "/apex/com.android.wifi/javalib/service-wifi.jar";
+ private static final String WIFI_SERVICE_CLASS =
+ "com.android.server.wifi.WifiService";
+ private static final String WIFI_SCANNING_SERVICE_CLASS =
+ "com.android.server.wifi.scanner.WifiScanningService";
+ private static final String WIFI_RTT_SERVICE_CLASS =
+ "com.android.server.wifi.rtt.RttService";
+ private static final String WIFI_AWARE_SERVICE_CLASS =
+ "com.android.server.wifi.aware.WifiAwareService";
+ private static final String WIFI_P2P_SERVICE_CLASS =
+ "com.android.server.wifi.p2p.WifiP2pService";
+ private static final String CONNECTIVITY_SERVICE_APEX_PATH =
+ "/apex/com.android.tethering/javalib/service-connectivity.jar";
+ private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS =
+ "com.android.server.ConnectivityServiceInitializer";
+ private static final String NETWORK_STATS_SERVICE_INITIALIZER_CLASS =
+ "com.android.server.NetworkStatsServiceInitializer";
+ private static final String UWB_APEX_SERVICE_JAR_PATH =
+ "/apex/com.android.uwb/javalib/service-uwb.jar";
+ private static final String UWB_SERVICE_CLASS = "com.android.server.uwb.UwbService";
+ private static final String BLUETOOTH_APEX_SERVICE_JAR_PATH =
+ "/apex/com.android.btservices/javalib/service-bluetooth.jar";
+ private static final String BLUETOOTH_SERVICE_CLASS =
+ "com.android.server.bluetooth.BluetoothService";
private static final String DEVICE_LOCK_SERVICE_CLASS =
"com.android.server.devicelock.DeviceLockService";
private static final String DEVICE_LOCK_APEX_PATH =
"/apex/com.android.devicelock/javalib/service-devicelock.jar";
-
private static final String PROFILING_SERVICE_LIFECYCLE_CLASS =
"android.os.profiling.ProfilingService$Lifecycle";
private static final String PROFILING_SERVICE_JAR_PATH =
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 3675834..69a88e9 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
@@ -16,7 +16,6 @@
package com.android.server.permission.access
-import android.permission.flags.Flags
import android.util.Slog
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
@@ -79,7 +78,7 @@
setPackageStates(packageStates)
setDisabledSystemPackageStates(disabledSystemPackageStates)
packageStates.forEach { (_, packageState) ->
- if (Flags.ignoreApexPermissions() && packageState.isApex) {
+ if (packageState.isApex) {
return@forEach
}
mutateAppIdPackageNames()
@@ -107,7 +106,7 @@
newState.mutateUserStatesNoWrite()[userId] = MutableUserState()
forEachSchemePolicy { with(it) { onUserAdded(userId) } }
newState.externalState.packageStates.forEach { (_, packageState) ->
- if (Flags.ignoreApexPermissions() && packageState.isApex) {
+ if (packageState.isApex) {
return@forEach
}
upgradePackageVersion(packageState, userId)
@@ -133,7 +132,7 @@
setPackageStates(packageStates)
setDisabledSystemPackageStates(disabledSystemPackageStates)
packageStates.forEach { (packageName, packageState) ->
- if (Flags.ignoreApexPermissions() && packageState.isApex) {
+ if (packageState.isApex) {
return@forEach
}
if (packageState.volumeUuid == volumeUuid) {
@@ -161,7 +160,7 @@
with(it) { onStorageVolumeMounted(volumeUuid, packageNames, isSystemUpdated) }
}
packageStates.forEach { (_, packageState) ->
- if (Flags.ignoreApexPermissions() && packageState.isApex) {
+ if (packageState.isApex) {
return@forEach
}
if (packageState.volumeUuid == volumeUuid) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 63fb468..87af841 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -81,7 +81,7 @@
override fun MutateStateScope.onUserAdded(userId: Int) {
newState.externalState.packageStates.forEach { (_, packageState) ->
- if (Flags.ignoreApexPermissions() && packageState.isApex) {
+ if (packageState.isApex) {
return@forEach
}
evaluateAllPermissionStatesForPackageAndUser(packageState, userId, null)
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 44ed3df..65feeb0 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
@@ -1445,7 +1445,7 @@
val packageStates = packageManagerLocal.withUnfilteredSnapshot().use { it.packageStates }
service.mutateState {
packageStates.forEach { (packageName, packageState) ->
- if (Flags.ignoreApexPermissions() && packageState.isApex) {
+ if (packageState.isApex) {
return@forEach
}
val androidPackage = packageState.androidPackage ?: return@forEach
@@ -1880,7 +1880,7 @@
packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
service.mutateState {
snapshot.packageStates.forEach { (_, packageState) ->
- if (Flags.ignoreApexPermissions() && packageState.isApex) {
+ if (packageState.isApex) {
return@forEach
}
with(policy) { resetRuntimePermissions(packageState.packageName, userId) }
@@ -1925,7 +1925,7 @@
packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
snapshot.packageStates.forEach { (_, packageState) ->
- if (Flags.ignoreApexPermissions() && packageState.isApex) {
+ if (packageState.isApex) {
return@forEach
}
val androidPackage = packageState.androidPackage ?: return@forEach
@@ -1943,7 +1943,7 @@
val permissions = service.getState { with(policy) { getPermissions() } }
packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
snapshot.packageStates.forEach packageStates@{ (_, packageState) ->
- if (Flags.ignoreApexPermissions() && packageState.isApex) {
+ if (packageState.isApex) {
return@packageStates
}
val androidPackage = packageState.androidPackage ?: return@packageStates
@@ -2072,7 +2072,7 @@
val appIdPackageNames = MutableIndexedMap<Int, MutableIndexedSet<String>>()
packageStates.forEach { (_, packageState) ->
- if (Flags.ignoreApexPermissions() && packageState.isApex) {
+ if (packageState.isApex) {
return@forEach
}
appIdPackageNames
@@ -2328,7 +2328,7 @@
isInstantApp: Boolean,
oldPackage: AndroidPackage?
) {
- if (Flags.ignoreApexPermissions() && packageState.isApex) {
+ if (packageState.isApex) {
return
}
@@ -2358,7 +2358,7 @@
params: PermissionManagerServiceInternal.PackageInstalledParams,
userId: Int
) {
- if (Flags.ignoreApexPermissions() && androidPackage.isApex) {
+ if (androidPackage.isApex) {
return
}
@@ -2414,7 +2414,7 @@
sharedUserPkgs: List<AndroidPackage>,
userId: Int
) {
- if (Flags.ignoreApexPermissions() && packageState.isApex) {
+ if (packageState.isApex) {
return
}
diff --git a/services/proguard.flags b/services/proguard.flags
index f84eff7..bf30781 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -29,11 +29,6 @@
public protected *;
}
-# Derivatives of SystemService and other services created via reflection
--keep,allowoptimization,allowaccessmodification class * extends com.android.server.SystemService {
- public <methods>;
-}
-
# Accessed from com.android.compos APEX
-keep,allowoptimization,allowaccessmodification class com.android.internal.art.ArtStatsLog {
public static void write(...);
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index 94ee0a8..a547d0f 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -17,9 +17,7 @@
package com.android.server.backup;
import static android.Manifest.permission.BACKUP;
-import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.Manifest.permission.PACKAGE_USAGE_STATS;
import static com.android.server.backup.testing.TransportData.backupTransport;
@@ -73,10 +71,7 @@
import org.robolectric.shadows.ShadowContextWrapper;
import java.io.File;
-import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
/** Tests for {@link BackupManagerService}. */
@RunWith(RobolectricTestRunner.class)
@@ -1461,67 +1456,12 @@
// Service tests
// ---------------------------------------------
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
- grantDumpPermissions();
- BackupManagerService backupManagerService = createSystemRegisteredService();
- File testFile = createTestFile();
- FileDescriptor fileDescriptor = new FileDescriptor();
- PrintWriter printWriter = new PrintWriter(testFile);
- String[] args = {"1", "2"};
- ShadowBinder.setCallingUserHandle(UserHandle.of(UserHandle.USER_SYSTEM));
-
- backupManagerService.dump(fileDescriptor, printWriter, args);
-
- verify(mUserSystemService).dump(fileDescriptor, printWriter, args);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception {
- grantDumpPermissions();
- BackupManagerService backupManagerService = createService();
- File testFile = createTestFile();
- FileDescriptor fileDescriptor = new FileDescriptor();
- PrintWriter printWriter = new PrintWriter(testFile);
- String[] args = {"1", "2"};
-
- backupManagerService.dump(fileDescriptor, printWriter, args);
-
- verify(mUserOneService, never()).dump(fileDescriptor, printWriter, args);
- }
-
- /** Test that 'dumpsys backup users' dumps the list of users registered in backup service*/
- @Test
- public void testDump_users_dumpsListOfRegisteredUsers() {
- grantDumpPermissions();
- BackupManagerService backupManagerService = createSystemRegisteredService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- StringWriter out = new StringWriter();
- PrintWriter writer = new PrintWriter(out);
- String[] args = {"users"};
-
- backupManagerService.dump(null, writer, args);
-
- writer.flush();
- assertEquals(
- String.format("%s %d %d\n", BackupManagerService.DUMP_RUNNING_USERS_MESSAGE,
- UserHandle.USER_SYSTEM, mUserOneId),
- out.toString());
- }
-
private File createTestFile() throws IOException {
File testFile = new File(mContext.getFilesDir(), "test");
testFile.createNewFile();
return testFile;
}
- private void grantDumpPermissions() {
- mShadowContext.grantPermissions(DUMP);
- mShadowContext.grantPermissions(PACKAGE_USAGE_STATS);
- }
-
/**
* Test that the backup services throws a {@link SecurityException} if the caller does not have
* INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 5897d76..0877146 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -920,6 +920,7 @@
@Test
public void testEvenDimmer() throws IOException {
when(mFlags.isEvenDimmerEnabled()).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_evenDimmerEnabled)).thenReturn(true);
setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ true));
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 66dd43a1..1666fef 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -133,8 +133,8 @@
import com.android.internal.R;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
+import com.android.internal.util.test.LocalServiceKeeperRule;
import com.android.modules.utils.testing.ExtendedMockitoRule;
-import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayManagerService.DeviceStateListener;
@@ -218,6 +218,9 @@
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+ @Rule(order = 2)
+ public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule();
+
private Context mContext;
private Resources mResources;
@@ -354,6 +357,7 @@
@Mock SensorManager mSensorManager;
@Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
@Mock PackageManagerInternal mMockPackageManagerInternal;
+ @Mock DisplayManagerInternal mMockDisplayManagerInternal;
@Mock DisplayAdapter mMockDisplayAdapter;
@Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@@ -371,20 +375,20 @@
when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(false);
mSetFlagsRule.disableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
- LocalServices.removeServiceForTest(InputManagerInternal.class);
- LocalServices.addService(InputManagerInternal.class, mMockInputManagerInternal);
- LocalServices.removeServiceForTest(WindowManagerInternal.class);
- LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal);
- LocalServices.removeServiceForTest(LightsManager.class);
- LocalServices.addService(LightsManager.class, mMockLightsManager);
- LocalServices.removeServiceForTest(SensorManagerInternal.class);
- LocalServices.addService(SensorManagerInternal.class, mMockSensorManagerInternal);
- LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
- LocalServices.addService(
+ mLocalServiceKeeperRule.overrideLocalService(
+ InputManagerInternal.class, mMockInputManagerInternal);
+ mLocalServiceKeeperRule.overrideLocalService(
+ WindowManagerInternal.class, mMockWindowManagerInternal);
+ mLocalServiceKeeperRule.overrideLocalService(
+ LightsManager.class, mMockLightsManager);
+ mLocalServiceKeeperRule.overrideLocalService(
+ SensorManagerInternal.class, mMockSensorManagerInternal);
+ mLocalServiceKeeperRule.overrideLocalService(
VirtualDeviceManagerInternal.class, mMockVirtualDeviceManagerInternal);
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
- // TODO: b/287945043
+ mLocalServiceKeeperRule.overrideLocalService(
+ PackageManagerInternal.class, mMockPackageManagerInternal);
+ mLocalServiceKeeperRule.overrideLocalService(
+ DisplayManagerInternal.class, mMockDisplayManagerInternal);
Display display = mock(Display.class);
when(display.getDisplayAdjustments()).thenReturn(new DisplayAdjustments());
when(display.getBrightnessInfo()).thenReturn(mock(BrightnessInfo.class));
@@ -1704,7 +1708,6 @@
* {@link VirtualDisplayConfig.Builder#setSurface(Surface)}
*/
@Test
- @FlakyTest(bugId = 127687569)
public void testCreateVirtualDisplay_setSurface() throws Exception {
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
registerDefaultDisplays(displayManager);
@@ -2629,11 +2632,19 @@
// Create default display device
createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
callback.waitForExpectedEvent();
+
+ callback.expectsEvent(EVENT_DISPLAY_ADDED);
FakeDisplayDevice displayDevice =
createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ callback.waitForExpectedEvent();
+
+ callback.expectsEvent(EVENT_DISPLAY_REMOVED);
+ displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ callback.waitForExpectedEvent();
+
+ callback.expectsEvent(EVENT_DISPLAY_ADDED);
LogicalDisplay display =
logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
- callback.expectsEvent(EVENT_DISPLAY_ADDED);
logicalDisplayMapper.setEnabledLocked(display, /* isEnabled= */ true);
logicalDisplayMapper.updateLogicalDisplays();
callback.waitForExpectedEvent();
@@ -2656,6 +2667,7 @@
LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
bs.registerCallbackWithEventMask(callback, STANDARD_DISPLAY_EVENTS);
+ displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
callback.expectsEvent(EVENT_DISPLAY_ADDED);
// Create default display device
createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
@@ -2669,7 +2681,6 @@
logicalDisplayMapper.setEnabledLocked(display, /* isEnabled= */ true);
logicalDisplayMapper.updateLogicalDisplays();
callback.waitForExpectedEvent();
- callback.clear();
assertThrows(SecurityException.class, () -> bs.disableConnectedDisplay(displayId));
}
@@ -2840,6 +2851,7 @@
float brightness1 = 0.3f;
float brightness2 = 0.45f;
+ waitForIdleHandler(mPowerHandler);
int userId1 = 123;
int userId2 = 456;
@@ -2849,8 +2861,8 @@
userInfo2.id = userId2;
when(mUserManager.getUserSerialNumber(userId1)).thenReturn(12345);
when(mUserManager.getUserSerialNumber(userId2)).thenReturn(45678);
- final SystemService.TargetUser from = new SystemService.TargetUser(userInfo1);
- final SystemService.TargetUser to = new SystemService.TargetUser(userInfo2);
+ final SystemService.TargetUser user1 = new SystemService.TargetUser(userInfo1);
+ final SystemService.TargetUser user2 = new SystemService.TargetUser(userInfo2);
// The same brightness will be restored for a user only if auto-brightness is off,
// otherwise the current lux will be used to determine the brightness.
@@ -2858,20 +2870,20 @@
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
- displayManager.onUserSwitching(to, from);
+ displayManager.onUserSwitching(/* from= */ user2, /* to= */ user1);
waitForIdleHandler(mPowerHandler);
displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, brightness1);
- displayManager.onUserSwitching(from, to);
+ displayManager.onUserSwitching(/* from= */ user1, /* to= */ user2);
waitForIdleHandler(mPowerHandler);
displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, brightness2);
- displayManager.onUserSwitching(to, from);
+ displayManager.onUserSwitching(/* from= */ user2, /* to= */ user1);
waitForIdleHandler(mPowerHandler);
assertEquals(brightness1,
displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
FLOAT_TOLERANCE);
- displayManager.onUserSwitching(from, to);
+ displayManager.onUserSwitching(/* from= */ user1, /* to= */ user2);
waitForIdleHandler(mPowerHandler);
assertEquals(brightness2,
displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
@@ -3005,6 +3017,7 @@
new DisplayManagerService(mContext, mBasicInjector);
displayManager.systemReady(false /* safeMode */);
+ displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
ArgumentMatcher<IntentFilter> matchesFilter =
(filter) -> Intent.ACTION_SETTING_RESTORED.equals(filter.getAction(0));
verify(mContext, times(0)).registerReceiver(any(BroadcastReceiver.class),
@@ -3370,7 +3383,7 @@
void waitForExpectedEvent(Duration timeout) {
try {
- assertWithMessage("Event '" + mExpectedEvent + "' is received.")
+ assertWithMessage("Expected '" + mExpectedEvent + "'")
.that(mLatch.await(timeout.toMillis(), TimeUnit.MILLISECONDS)).isTrue();
} catch (InterruptedException ex) {
throw new AssertionError("Waiting for expected event interrupted", ex);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
index b182cce..88c0daa 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
@@ -19,11 +19,13 @@
import android.content.Context
import android.content.ContextWrapper
import android.hardware.display.BrightnessInfo
+import android.util.SparseArray
import android.view.Display
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.SmallTest
import com.android.server.display.DisplayDeviceConfig
import com.android.server.display.feature.DisplayManagerFlags
+import com.android.server.display.mode.DisplayModeDirector.DisplayDeviceConfigProvider
import com.android.server.testutils.TestHandler
import com.google.common.truth.Truth.assertThat
import com.google.testing.junit.testparameterinjector.TestParameter
@@ -48,6 +50,7 @@
private val mockInjector = mock<DisplayModeDirector.Injector>()
private val mockFlags = mock<DisplayManagerFlags>()
private val mockDeviceConfig = mock<DisplayDeviceConfig>()
+ private val mockDisplayDeviceConfigProvider = mock<DisplayDeviceConfigProvider>()
private val testHandler = TestHandler(null)
@@ -61,9 +64,13 @@
setUpLowBrightnessZone()
whenever(mockFlags.isVsyncLowLightVoteEnabled).thenReturn(testCase.vsyncLowLightVoteEnabled)
val displayModeDirector = DisplayModeDirector(
- spyContext, testHandler, mockInjector, mockFlags)
+ spyContext, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider)
+ val ddcByDisplay = SparseArray<DisplayDeviceConfig>()
+ whenever(mockDeviceConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported)
+ ddcByDisplay.put(Display.DEFAULT_DISPLAY, mockDeviceConfig)
+ displayModeDirector.injectDisplayDeviceConfigByDisplay(ddcByDisplay)
val brightnessObserver = displayModeDirector.BrightnessObserver(
- spyContext, testHandler, mockInjector, testCase.vrrSupported, mockFlags)
+ spyContext, testHandler, mockInjector, mockFlags)
brightnessObserver.onRefreshRateSettingChangedLocked(0.0f, 120.0f)
brightnessObserver.updateBlockingZoneThresholds(mockDeviceConfig, false)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index fbc38a2..4591d91 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -312,6 +312,8 @@
public DisplayManagerInternal mDisplayManagerInternalMock;
@Mock
private DisplayManagerFlags mDisplayManagerFlags;
+ @Mock
+ private DisplayModeDirector.DisplayDeviceConfigProvider mDisplayDeviceConfigProvider;
@Rule
public final ExtendedMockitoRule mExtendedMockitoRule =
@@ -338,8 +340,6 @@
.thenReturn(false);
when(resources.getBoolean(R.bool.config_refreshRateSynchronizationEnabled))
.thenReturn(false);
- when(resources.getBoolean(R.bool.config_supportsDvrr))
- .thenReturn(false);
when(resources.getInteger(R.integer.config_displayWhiteBalanceBrightnessFilterHorizon))
.thenReturn(10000);
when(resources.getInteger(R.integer.config_defaultPeakRefreshRate))
@@ -393,41 +393,88 @@
private DisplayModeDirector createDirectorFromRefreshRateArray(
float[] refreshRates, int baseModeId, float defaultRefreshRate) {
- Display.Mode[] modes = new Display.Mode[refreshRates.length];
- Display.Mode defaultMode = null;
- for (int i = 0; i < refreshRates.length; i++) {
- modes[i] = new Display.Mode(
- /*modeId=*/baseModeId + i, /*width=*/1000, /*height=*/1000, refreshRates[i]);
- if (refreshRates[i] == defaultRefreshRate) {
- defaultMode = modes[i];
- }
- }
+ return createDirectorFromRefreshRateArray(refreshRates, baseModeId, defaultRefreshRate,
+ new int[]{DISPLAY_ID});
+ }
+
+ private DisplayModeDirector createDirectorFromRefreshRateArray(
+ float[] refreshRates, int baseModeId, float defaultRefreshRate, int[] displayIds) {
+ Display.Mode[] modes = createDisplayModes(refreshRates, baseModeId);
+ Display.Mode defaultMode = getDefaultMode(modes, defaultRefreshRate);
+
assertThat(defaultMode).isNotNull();
- return createDirectorFromModeArray(modes, defaultMode);
+ return createDirectorFromModeArray(modes, defaultMode, displayIds);
}
private DisplayModeDirector createDirectorFromModeArray(Display.Mode[] modes,
Display.Mode defaultMode) {
+ return createDirectorFromModeArray(modes, defaultMode, new int[]{DISPLAY_ID});
+ }
+
+ private DisplayModeDirector createDirectorFromModeArray(Display.Mode[] modes,
+ Display.Mode defaultMode, int[] displayIds) {
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
+ new DisplayModeDirector(mContext, mHandler, mInjector,
+ mDisplayManagerFlags, mDisplayDeviceConfigProvider);
director.setLoggingEnabled(true);
- SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
- supportedModesByDisplay.put(DISPLAY_ID, modes);
- director.injectSupportedModesByDisplay(supportedModesByDisplay);
- SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
- defaultModesByDisplay.put(DISPLAY_ID, defaultMode);
- director.injectDefaultModeByDisplay(defaultModesByDisplay);
+ setupModesForDisplays(director, displayIds , modes, defaultMode);
return director;
}
private DisplayModeDirector createDirectorFromFpsRange(int minFps, int maxFps) {
+ return createDirectorFromRefreshRateArray(
+ createRefreshRateRanges(minFps, maxFps),
+ /*baseModeId=*/minFps,
+ /*defaultRefreshRate=*/minFps,
+ new int[]{DISPLAY_ID});
+ }
+
+ private DisplayModeDirector createDirectorFromFpsRange(
+ int minFps, int maxFps, int[] displayIds) {
+ return createDirectorFromRefreshRateArray(
+ createRefreshRateRanges(minFps, maxFps),
+ /*baseModeId=*/minFps,
+ /*defaultRefreshRate=*/minFps,
+ displayIds);
+ }
+
+ private void setupModesForDisplays(DisplayModeDirector director, int[] displayIds,
+ Display.Mode[] modes, Display.Mode defaultMode) {
+ SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
+ SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
+ for (int displayId: displayIds) {
+ supportedModesByDisplay.put(displayId, modes);
+ defaultModesByDisplay.put(displayId, defaultMode);
+ }
+ director.injectSupportedModesByDisplay(supportedModesByDisplay);
+ director.injectDefaultModeByDisplay(defaultModesByDisplay);
+ }
+
+ private Display.Mode[] createDisplayModes(float[] refreshRates, int baseModeId) {
+ Display.Mode[] modes = new Display.Mode[refreshRates.length];
+ for (int i = 0; i < refreshRates.length; i++) {
+ modes[i] = new Display.Mode(
+ /*modeId=*/baseModeId + i, /*width=*/1000, /*height=*/1000, refreshRates[i]);
+ }
+ return modes;
+ }
+
+ private Display.Mode getDefaultMode(Display.Mode[] modes, float defaultRefreshRate) {
+ for (Display.Mode mode : modes) {
+ if (mode.getRefreshRate() == defaultRefreshRate) {
+ return mode;
+ }
+ }
+ return null;
+ }
+
+ private float[] createRefreshRateRanges(int minFps, int maxFps) {
int numRefreshRates = maxFps - minFps + 1;
float[] refreshRates = new float[numRefreshRates];
for (int i = 0; i < numRefreshRates; i++) {
refreshRates[i] = minFps + i;
}
- return createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/minFps,
- /*defaultRefreshRate=*/minFps);
+ return refreshRates;
}
@Test
@@ -1102,7 +1149,8 @@
@Test
public void testStaleAppRequestSize() {
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
+ new DisplayModeDirector(mContext, mHandler, mInjector,
+ mDisplayManagerFlags, mDisplayDeviceConfigProvider);
Display.Mode[] modes = new Display.Mode[] {
new Display.Mode(1, 1280, 720, 60),
};
@@ -1353,7 +1401,8 @@
when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
.thenReturn(true);
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
+ new DisplayModeDirector(mContext, mHandler, mInjector,
+ mDisplayManagerFlags, mDisplayDeviceConfigProvider);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
Display.Mode[] modes1 = new Display.Mode[] {
@@ -1764,7 +1813,8 @@
when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
.thenReturn(true);
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
+ new DisplayModeDirector(mContext, mHandler, mInjector,
+ mDisplayManagerFlags, mDisplayDeviceConfigProvider);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
Display.Mode[] modes1 = new Display.Mode[] {
@@ -1844,7 +1894,8 @@
when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
.thenReturn(true);
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
+ new DisplayModeDirector(mContext, mHandler, mInjector,
+ mDisplayManagerFlags, mDisplayDeviceConfigProvider);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
mInjector.mDisplayInfo.supportedModes = new Display.Mode[] {
new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
@@ -1893,6 +1944,7 @@
mInjector.mDisplayInfo.displayId = DISPLAY_ID_2;
DisplayModeDirector director = createDirectorFromModeArray(TEST_MODES, DEFAULT_MODE_60);
+ director.start(createMockSensorManager());
SparseArray<Vote> votes = new SparseArray<>();
votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 50f));
@@ -1913,7 +1965,8 @@
when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
.thenReturn(true);
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
+ new DisplayModeDirector(mContext, mHandler, mInjector,
+ mDisplayManagerFlags, mDisplayDeviceConfigProvider);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
Display.Mode[] modes1 = new Display.Mode[] {
@@ -1993,7 +2046,8 @@
when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
.thenReturn(true);
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
+ new DisplayModeDirector(mContext, mHandler, mInjector,
+ mDisplayManagerFlags, mDisplayDeviceConfigProvider);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
mInjector.mDisplayInfo.supportedModes = new Display.Mode[] {
new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
@@ -2031,7 +2085,8 @@
when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
.thenReturn(true);
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
+ new DisplayModeDirector(mContext, mHandler, mInjector,
+ mDisplayManagerFlags, mDisplayDeviceConfigProvider);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
Display.Mode[] modes1 = new Display.Mode[] {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
index 92016df..2d317af 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
@@ -39,6 +39,7 @@
import android.content.ContextWrapper;
import android.content.res.Resources;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.Looper;
import android.provider.DeviceConfigInterface;
@@ -112,6 +113,8 @@
private Resources mResources;
@Mock
private DisplayManagerFlags mDisplayManagerFlags;
+ @Mock
+ private DisplayModeDirector.DisplayDeviceConfigProvider mDisplayDeviceConfigProvider;
private int mExternalDisplayUserPreferredModeId = INVALID_MODE_ID;
private int mInternalDisplayUserPreferredModeId = INVALID_MODE_ID;
private Display mDefaultDisplay;
@@ -426,8 +429,12 @@
return true;
}).when(mInjector).getDisplayInfo(eq(EXTERNAL_DISPLAY), /*displayInfo=*/ any());
- doAnswer(c -> mock(SensorManagerInternal.class)).when(mInjector).getSensorManagerInternal();
+ doAnswer(c -> mock(SensorManagerInternal.class))
+ .when(mInjector).getSensorManagerInternal();
doAnswer(c -> mock(DeviceConfigInterface.class)).when(mInjector).getDeviceConfig();
+ doAnswer(c -> mock(DisplayManagerInternal.class))
+ .when(mInjector).getDisplayManagerInternal();
+
mDefaultDisplay = mock(Display.class);
when(mDefaultDisplay.getDisplayId()).thenReturn(DEFAULT_DISPLAY);
@@ -441,7 +448,8 @@
when(mInjector.getDisplays()).thenReturn(new Display[] {mDefaultDisplay, mExternalDisplay});
- mDmd = new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
+ mDmd = new DisplayModeDirector(mContext, mHandler, mInjector,
+ mDisplayManagerFlags, mDisplayDeviceConfigProvider);
mDmd.start(null);
assertThat(mObserver).isNotNull();
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
index 230317b..3c87261 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
@@ -19,10 +19,14 @@
import android.content.Context
import android.content.ContextWrapper
import android.provider.Settings
+import android.util.SparseArray
+import android.view.Display
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.SmallTest
import com.android.internal.util.test.FakeSettingsProvider
+import com.android.server.display.DisplayDeviceConfig
import com.android.server.display.feature.DisplayManagerFlags
+import com.android.server.display.mode.DisplayModeDirector.DisplayDeviceConfigProvider
import com.android.server.testutils.TestHandler
import com.google.common.truth.Truth.assertThat
import com.google.testing.junit.testparameterinjector.TestParameter
@@ -39,7 +43,6 @@
@SmallTest
@RunWith(TestParameterInjector::class)
class SettingsObserverTest {
-
@get:Rule
val mockitoRule = MockitoJUnit.rule()
@@ -49,6 +52,8 @@
private lateinit var spyContext: Context
private val mockInjector = mock<DisplayModeDirector.Injector>()
private val mockFlags = mock<DisplayManagerFlags>()
+ private val mockDeviceConfig = mock<DisplayDeviceConfig>()
+ private val mockDisplayDeviceConfigProvider = mock<DisplayDeviceConfigProvider>()
private val testHandler = TestHandler(null)
@@ -67,9 +72,13 @@
spyContext.contentResolver, Settings.Global.LOW_POWER_MODE, lowPowerModeSetting)
val displayModeDirector = DisplayModeDirector(
- spyContext, testHandler, mockInjector, mockFlags)
+ spyContext, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider)
+ val ddcByDisplay = SparseArray<DisplayDeviceConfig>()
+ whenever(mockDeviceConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported)
+ ddcByDisplay.put(Display.DEFAULT_DISPLAY, mockDeviceConfig)
+ displayModeDirector.injectDisplayDeviceConfigByDisplay(ddcByDisplay)
val settingsObserver = displayModeDirector.SettingsObserver(
- spyContext, testHandler, testCase.dvrrSupported, mockFlags)
+ spyContext, testHandler, mockFlags)
settingsObserver.onChange(
false, Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), 1)
@@ -79,7 +88,7 @@
}
enum class SettingsObserverTestCase(
- val dvrrSupported: Boolean,
+ val vrrSupported: Boolean,
val vsyncLowPowerVoteEnabled: Boolean,
val lowPowerModeEnabled: Boolean,
internal val expectedVote: Vote?
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
index edee8cd..124ae20 100644
--- a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
@@ -282,6 +282,14 @@
.getActiveNotifications();
}
+ private void setupNullNotifications() {
+ // Setup Notification Values
+ StatusBarNotification[] mNotifications = new StatusBarNotification[] { null, null};
+ doReturn(mNotifications)
+ .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+ .getActiveNotifications();
+ }
+
private MediaProjectionInfo createMediaProjectionInfo() {
return new MediaProjectionInfo(SCREEN_RECORDER_PACKAGE, Process.myUserHandle(), null);
}
@@ -502,6 +510,17 @@
}
@Test
+ public void nlsOnListenerConnected_nullNotifications_noBlockedPackages() {
+ setupNullNotifications();
+ mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
+
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
public void nlsOnListenerConnected_nullRankingMap_noBlockedPackages() {
// Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
// as non-sensitive
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
index 783971a..89b48ba 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -16,9 +16,18 @@
package com.android.server.am;
+import static android.os.Process.INVALID_UID;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
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.am.PendingIntentRecord.CANCEL_REASON_NULL;
+import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_ONE_SHOT_SENT;
+import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_OWNER_CANCELED;
+import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_OWNER_FORCE_STOPPED;
+import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_SUPERSEDED;
+import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_USER_STOPPED;
+import static com.android.server.am.PendingIntentRecord.cancelReasonToString;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -34,6 +43,7 @@
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.os.Looper;
+import android.os.UserHandle;
import androidx.test.runner.AndroidJUnit4;
@@ -54,6 +64,7 @@
private static final String TEST_PACKAGE_NAME = "test-package-1";
private static final String TEST_FEATURE_ID = "test-feature-1";
private static final int TEST_CALLING_UID = android.os.Process.myUid();
+ private static final int TEST_USER_ID = 0;
private static final Intent[] TEST_INTENTS = new Intent[]{new Intent("com.test.intent")};
@Mock
@@ -92,7 +103,7 @@
private PendingIntentRecord createPendingIntentRecord(int flags) {
return mPendingIntentController.getIntentSender(ActivityManager.INTENT_SENDER_BROADCAST,
- TEST_PACKAGE_NAME, TEST_FEATURE_ID, TEST_CALLING_UID, 0, null, null, 0,
+ TEST_PACKAGE_NAME, TEST_FEATURE_ID, TEST_CALLING_UID, TEST_USER_ID, null, null, 0,
TEST_INTENTS, null, flags, null);
}
@@ -126,6 +137,54 @@
piCaptor.getValue().getTarget());
}
+ @Test
+ public void testCancellationReason() {
+ {
+ final PendingIntentRecord pir = createPendingIntentRecord(0);
+ assertCancelReason(CANCEL_REASON_NULL, pir.cancelReason);
+ }
+
+ {
+ final PendingIntentRecord pir = createPendingIntentRecord(0);
+ mPendingIntentController.cancelIntentSender(pir);
+ assertCancelReason(CANCEL_REASON_OWNER_CANCELED, pir.cancelReason);
+ }
+
+ {
+ final PendingIntentRecord pir = createPendingIntentRecord(0);
+ createPendingIntentRecord(PendingIntent.FLAG_CANCEL_CURRENT);
+ assertCancelReason(CANCEL_REASON_SUPERSEDED, pir.cancelReason);
+ }
+
+ {
+ final PendingIntentRecord pir = createPendingIntentRecord(PendingIntent.FLAG_ONE_SHOT);
+ pir.send(0, null, null, null, null, null, null);
+ assertCancelReason(CANCEL_REASON_ONE_SHOT_SENT, pir.cancelReason);
+ }
+
+ {
+ final PendingIntentRecord pir = createPendingIntentRecord(0);
+ mPendingIntentController.removePendingIntentsForPackage(TEST_PACKAGE_NAME,
+ TEST_USER_ID, UserHandle.getAppId(TEST_CALLING_UID), true,
+ CANCEL_REASON_OWNER_FORCE_STOPPED);
+ assertCancelReason(CANCEL_REASON_OWNER_FORCE_STOPPED, pir.cancelReason);
+ }
+
+ {
+ final PendingIntentRecord pir = createPendingIntentRecord(0);
+ mPendingIntentController.removePendingIntentsForPackage(null,
+ TEST_USER_ID, INVALID_UID, true,
+ CANCEL_REASON_USER_STOPPED);
+ assertCancelReason(CANCEL_REASON_USER_STOPPED, pir.cancelReason);
+ }
+ }
+
+ private void assertCancelReason(int expectedReason, int actualReason) {
+ final String errMsg = "Expected: " + cancelReasonToString(expectedReason)
+ + "; Actual: " + cancelReasonToString(actualReason);
+ assertEquals(errMsg, expectedReason, actualReason);
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java
index b203cf6..c4a0423 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -38,7 +38,6 @@
import static org.mockito.Mockito.when;
import android.Manifest;
-import android.annotation.UserIdInt;
import android.app.backup.BackupManager;
import android.app.backup.ISelectBackupTransportCallback;
import android.app.job.JobScheduler;
@@ -59,10 +58,12 @@
import androidx.test.filters.SmallTest;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
import com.android.server.backup.utils.RandomAccessFileUtils;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -74,6 +75,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.io.Writer;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -85,8 +87,6 @@
"class");
private static final int NON_SYSTEM_USER = UserHandle.USER_SYSTEM + 1;
- @UserIdInt
- private int mUserId;
@Mock
private UserBackupManagerService mSystemUserBackupManagerService;
@Mock
@@ -94,8 +94,6 @@
@Mock
private Context mContextMock;
@Mock
- private PrintWriter mPrintWriterMock;
- @Mock
private UserManager mUserManagerMock;
@Mock
private UserInfo mUserInfoMock;
@@ -104,6 +102,7 @@
private BackupManagerServiceTestable mService;
private BackupManagerService.Lifecycle mServiceLifecycle;
+ private FakePrintWriter mFakePrintWriter;
private static File sTestDir;
private MockitoSession mSession;
@@ -114,6 +113,7 @@
this)
.strictness(Strictness.LENIENT)
.spyStatic(UserBackupManagerService.class)
+ .spyStatic(DumpUtils.class)
.startMocking();
doReturn(mSystemUserBackupManagerService).when(
() -> UserBackupManagerService.createAndInitializeService(
@@ -122,8 +122,7 @@
() -> UserBackupManagerService.createAndInitializeService(eq(NON_SYSTEM_USER),
any(), any(), any()));
- mUserId = UserHandle.USER_SYSTEM;
-
+ when(mNonSystemUserBackupManagerService.getUserId()).thenReturn(NON_SYSTEM_USER);
when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
when(mUserManagerMock.getUserInfo(NON_SYSTEM_USER)).thenReturn(mUserInfoMock);
// Null main user means there is no main user on the device.
@@ -139,6 +138,8 @@
when(mContextMock.getSystemService(Context.JOB_SCHEDULER_SERVICE))
.thenReturn(mock(JobScheduler.class));
+
+ mFakePrintWriter = new FakePrintWriter();
}
@After
@@ -552,7 +553,8 @@
}
};
- mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
+ mService.selectBackupTransportAsyncForUser(
+ UserHandle.USER_SYSTEM, TRANSPORT_COMPONENT_NAME, listener);
assertEquals(BackupManager.ERROR_BACKUP_NOT_ALLOWED, (int) future.get(5, TimeUnit.SECONDS));
}
@@ -560,9 +562,10 @@
@Test
public void selectBackupTransportAsyncForUser_beforeUserUnlockedWithNullListener_doesNotThrow()
throws Exception {
- createBackupManagerServiceAndUnlockSystemUser();
+ mService = new BackupManagerServiceTestable(mContextMock);
- mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
+ mService.selectBackupTransportAsyncForUser(
+ UserHandle.USER_SYSTEM, TRANSPORT_COMPONENT_NAME, null);
// No crash.
}
@@ -570,13 +573,11 @@
@Test
public void selectBackupTransportAsyncForUser_beforeUserUnlockedListenerThrowing_doesNotThrow()
throws Exception {
- createBackupManagerServiceAndUnlockSystemUser();
-
+ mService = new BackupManagerServiceTestable(mContextMock);
ISelectBackupTransportCallback.Stub listener =
new ISelectBackupTransportCallback.Stub() {
@Override
- public void onSuccess(String transportName) {
- }
+ public void onSuccess(String transportName) {}
@Override
public void onFailure(int reason) throws RemoteException {
@@ -584,55 +585,91 @@
}
};
- mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
+ mService.selectBackupTransportAsyncForUser(
+ UserHandle.USER_SYSTEM, TRANSPORT_COMPONENT_NAME, listener);
// No crash.
}
@Test
- public void dump_callerDoesNotHaveDumpPermission_ignored() {
+ public void dump_callerDoesNotHaveDumpOrUsageStatsPermission_ignored() {
+ mockDumpPermissionsGranted(false);
createBackupManagerServiceAndUnlockSystemUser();
- when(mContextMock.checkCallingOrSelfPermission(
- Manifest.permission.DUMP)).thenReturn(
- PackageManager.PERMISSION_DENIED);
+ when(mContextMock.checkCallingOrSelfPermission(Manifest.permission.DUMP))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
- mService.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
+ mService.dump(mFileDescriptorStub, mFakePrintWriter, new String[0]);
verify(mSystemUserBackupManagerService, never()).dump(any(), any(), any());
verify(mNonSystemUserBackupManagerService, never()).dump(any(), any(), any());
}
@Test
- public void dump_callerDoesNotHavePackageUsageStatsPermission_ignored() {
- createBackupManagerServiceAndUnlockSystemUser();
- when(mContextMock.checkCallingOrSelfPermission(
- Manifest.permission.PACKAGE_USAGE_STATS)).thenReturn(
- PackageManager.PERMISSION_DENIED);
-
- mService.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
-
- verify(mSystemUserBackupManagerService, never()).dump(any(), any(), any());
- verify(mNonSystemUserBackupManagerService, never()).dump(any(), any(), any());
- }
-
- /**
- * Test that {@link BackupManagerService#dump(FileDescriptor, PrintWriter, String[])} dumps
- * system user information before non-system user information.
- */
- @Test
- public void testDump_systemUserFirst() {
+ public void dump_forOneUser_callerDoesNotHaveInteractAcrossUsersFullPermission_ignored() {
+ mockDumpPermissionsGranted(true);
createBackupManagerServiceAndUnlockSystemUser();
mService.setBackupServiceActive(NON_SYSTEM_USER, true);
simulateUserUnlocked(NON_SYSTEM_USER);
+ doThrow(new SecurityException())
+ .when(mContextMock)
+ .enforceCallingOrSelfPermission(
+ eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyString());
+
+ String[] args = new String[] {"--user", Integer.toString(NON_SYSTEM_USER)};
+ Assert.assertThrows(
+ SecurityException.class,
+ () -> mService.dump(mFileDescriptorStub, mFakePrintWriter, args));
+
+ verify(mNonSystemUserBackupManagerService, never()).dump(any(), any(), any());
+ }
+
+ @Test
+ public void dump_forOneUser_callerHasInteractAcrossUsersFullPermission_dumpsSpecifiedUser() {
+ mockDumpPermissionsGranted(true);
+ createBackupManagerServiceAndUnlockSystemUser();
+ mService.setBackupServiceActive(NON_SYSTEM_USER, true);
+ simulateUserUnlocked(NON_SYSTEM_USER);
+
+ String[] args = new String[] {"--user", Integer.toString(UserHandle.USER_SYSTEM)};
+ mService.dump(mFileDescriptorStub, mFakePrintWriter, args);
+
+ verify(mSystemUserBackupManagerService).dump(any(), any(), any());
+ }
+
+ @Test
+ public void dump_users_callerHasInteractAcrossUsersFullPermission_dumpsUsers() {
+ mockDumpPermissionsGranted(true);
+ createBackupManagerServiceAndUnlockSystemUser();
+ mService.setBackupServiceActive(NON_SYSTEM_USER, true);
+ simulateUserUnlocked(NON_SYSTEM_USER);
+
+ String[] args = new String[] {"users"};
+ mService.dump(mFileDescriptorStub, mFakePrintWriter, args);
+
+ // Check that dump() invocations are not called on user's Backup service,
+ // as 'dumpsys backup users' only list users for whom Backup service is running.
+ verify(mSystemUserBackupManagerService, never()).dump(any(), any(), any());
+ verify(mNonSystemUserBackupManagerService, never()).dump(any(), any(), any());
+ assertThat(mFakePrintWriter.mPrintedSoFar)
+ .isEqualTo("Backup Manager is running for users: 0 1");
+ }
+
+ @Test
+ public void dump_allUsers_dumpsSystemUserFirst() {
+ mockDumpPermissionsGranted(true);
+ createBackupManagerServiceAndUnlockSystemUser();
+ mService.setBackupServiceActive(NON_SYSTEM_USER, true);
+ simulateUserUnlocked(NON_SYSTEM_USER);
+
String[] args = new String[0];
- mService.dumpWithoutCheckingPermission(mFileDescriptorStub, mPrintWriterMock, args);
+ mService.dump(mFileDescriptorStub, mFakePrintWriter, args);
InOrder inOrder =
inOrder(mSystemUserBackupManagerService, mNonSystemUserBackupManagerService);
inOrder.verify(mSystemUserBackupManagerService)
- .dump(mFileDescriptorStub, mPrintWriterMock, args);
+ .dump(mFileDescriptorStub, mFakePrintWriter, args);
inOrder.verify(mNonSystemUserBackupManagerService)
- .dump(mFileDescriptorStub, mPrintWriterMock, args);
+ .dump(mFileDescriptorStub, mFakePrintWriter, args);
inOrder.verifyNoMoreInteractions();
}
@@ -753,6 +790,11 @@
return new File(sTestDir, "rememberActivated-" + userId);
}
+ private static void mockDumpPermissionsGranted(boolean granted) {
+ doReturn(granted)
+ .when(() -> DumpUtils.checkDumpAndUsageStatsPermission(any(), any(), any()));
+ }
+
private static class BackupManagerServiceTestable extends BackupManagerService {
static boolean sBackupDisabled = false;
static int sCallingUserId = -1;
@@ -803,4 +845,17 @@
runnable.run();
}
}
+
+ private static class FakePrintWriter extends PrintWriter {
+ String mPrintedSoFar = "";
+
+ FakePrintWriter() {
+ super(Writer.nullWriter());
+ }
+
+ @Override
+ public void print(String s) {
+ mPrintedSoFar = mPrintedSoFar + s;
+ }
+ }
}
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 671472d..6df4907 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
@@ -1978,7 +1978,7 @@
}
@Test
- public void testIsWithinQuotaLocked_UnderDuration_OverJobCountRateLimitWindow() {
+ public void testIsWithinQuotaLocked_UnderDuration_OverJobCount() {
setDischarging();
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
@@ -2021,7 +2021,7 @@
}
@Test
- public void testIsWithinQuotaLocked_OverDuration_OverJobCountRateLimitWindow() {
+ public void testIsWithinQuotaLocked_OverDuration_OverJobCount() {
setDischarging();
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
@@ -2167,73 +2167,6 @@
}
@Test
- public void testIsWithinQuotaLocked_UnderDuration_OverJobCountInWindow() {
- setDischarging();
-
- JobStatus jobRunning = createJobStatus(
- "testIsWithinQuotaLocked_UnderDuration_OverJobCountInWindow", 1);
- JobStatus jobPending = createJobStatus(
- "testIsWithinQuotaLocked_UnderDuration_OverJobCountInWindow", 2);
- setStandbyBucket(WORKING_INDEX, jobRunning, jobPending);
-
- setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10);
-
- long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 9), false);
-
- final ExecutionStats stats;
- synchronized (mQuotaController.mLock) {
- stats = mQuotaController.getExecutionStatsLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(10, stats.jobCountLimit);
- assertEquals(9, stats.bgJobCountInWindow);
- }
-
- when(mJobSchedulerService.isCurrentlyRunningLocked(jobRunning)).thenReturn(true);
- when(mJobSchedulerService.isCurrentlyRunningLocked(jobPending)).thenReturn(false);
-
- InOrder inOrder = inOrder(mJobSchedulerService);
- trackJobs(jobRunning, jobPending);
- // UID in the background.
- setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
- // Start the job.
- synchronized (mQuotaController.mLock) {
- mQuotaController.prepareForExecutionLocked(jobRunning);
- }
-
- advanceElapsedClock(MINUTE_IN_MILLIS);
- // Wait for some extra time to allow for job processing.
- ArraySet<JobStatus> expected = new ArraySet<>();
- expected.add(jobPending);
- inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged(eq(expected));
-
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController.isWithinQuotaLocked(jobRunning));
- assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
- assertTrue(jobRunning.isReady());
- assertFalse(mQuotaController.isWithinQuotaLocked(jobPending));
- assertFalse(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
- assertFalse(jobPending.isReady());
- assertEquals(10, stats.bgJobCountInWindow);
- }
-
- advanceElapsedClock(MINUTE_IN_MILLIS);
- synchronized (mQuotaController.mLock) {
- mQuotaController.maybeStopTrackingJobLocked(jobRunning, null);
- }
-
- synchronized (mQuotaController.mLock) {
- assertFalse(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(10, stats.bgJobCountInWindow);
- }
- }
-
- @Test
public void testIsWithinQuotaLocked_TimingSession() {
setDischarging();
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
@@ -4718,7 +4651,7 @@
// Handler is told to check when the quota will be consumed, not when the initial
// remaining time is over.
verify(handler, atLeast(1)).sendMessageDelayed(
- argThat(msg -> msg.what == QuotaController.MSG_REACHED_TIME_QUOTA),
+ argThat(msg -> msg.what == QuotaController.MSG_REACHED_QUOTA),
eq(10 * SECOND_IN_MILLIS));
verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs));
@@ -6685,7 +6618,7 @@
// Handler is told to check when the quota will be consumed, not when the initial
// remaining time is over.
verify(handler, atLeast(1)).sendMessageDelayed(
- argThat(msg -> msg.what == QuotaController.MSG_REACHED_EJ_TIME_QUOTA),
+ argThat(msg -> msg.what == QuotaController.MSG_REACHED_EJ_QUOTA),
eq(10 * SECOND_IN_MILLIS));
verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 759a974..79f1574 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -34,6 +34,7 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -58,6 +59,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.util.Log;
@@ -139,7 +141,8 @@
.mockStatic(Settings.Secure.class)
.build();
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
+ SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
private final Object mPackagesLock = new Object();
private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation()
@@ -584,10 +587,12 @@
public void testAutoLockPrivateProfile() {
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
+ int mainUser = mUms.getMainUserId();
+ assumeTrue(mUms.canAddPrivateProfile(mainUser));
UserManagerService mSpiedUms = spy(mUms);
UserInfo privateProfileUser =
mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
- USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
+ USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null);
Mockito.doNothing().when(mSpiedUms).setQuietModeEnabledAsync(
eq(privateProfileUser.getUserHandle().getIdentifier()), eq(true), any(),
any());
@@ -604,10 +609,12 @@
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
+ int mainUser = mUms.getMainUserId();
+ assumeTrue(mUms.canAddPrivateProfile(mainUser));
UserManagerService mSpiedUms = spy(mUms);
UserInfo privateProfileUser =
mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
- USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
+ USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null);
mockAutoLockForPrivateSpace(Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK);
Mockito.doNothing().when(mSpiedUms).setQuietModeEnabledAsync(
eq(privateProfileUser.getUserHandle().getIdentifier()), eq(true), any(),
@@ -625,6 +632,7 @@
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
+ assumeTrue(mUms.canAddPrivateProfile(0));
UserManagerService mSpiedUms = spy(mUms);
UserInfo privateProfileUser =
mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
@@ -644,10 +652,12 @@
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
mSetFlagsRule.disableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
+ int mainUser = mUms.getMainUserId();
+ assumeTrue(mUms.canAddPrivateProfile(mainUser));
UserManagerService mSpiedUms = spy(mUms);
UserInfo privateProfileUser =
mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
- USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
+ USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null);
mSpiedUms.tryAutoLockingPrivateSpaceOnKeyguardChanged(true);
@@ -664,13 +674,15 @@
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
+ int mainUser = mUms.getMainUserId();
+ assumeTrue(mUms.canAddPrivateProfile(mainUser));
UserManagerService mSpiedUms = spy(mUms);
mockAutoLockForPrivateSpace(Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY);
when(mPowerManager.isInteractive()).thenReturn(false);
UserInfo privateProfileUser =
mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
- USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
+ USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null);
Mockito.doNothing().when(mSpiedUms).scheduleMessageToAutoLockPrivateSpace(
eq(privateProfileUser.getUserHandle().getIdentifier()), any(),
anyLong());
@@ -702,8 +714,10 @@
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
+ int mainUser = mUms.getMainUserId();
+ assumeTrue(mUms.canAddPrivateProfile(mainUser));
mUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
- USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
+ USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null);
// Set the preference to auto lock on device lock
mUms.setOrUpdateAutoLockPreferenceForPrivateProfile(
@@ -754,6 +768,7 @@
mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_HIDING_PROFILES);
+ assumeTrue(mUms.canAddPrivateProfile(0));
UserInfo privateProfileUser =
mUms.createProfileForUserEvenWhenDisallowedWithThrow("TestPrivateProfile",
USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
@@ -763,23 +778,23 @@
}
@Test
+ @RequiresFlagsEnabled({android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION, Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
public void testCreatePrivateProfileOnHeadlessSystemUser_shouldAllowCreation() {
- mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
- android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
- mSetFlagsRule.enableFlags(Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION);
UserManagerService mSpiedUms = spy(mUms);
+ assumeTrue(mUms.isHeadlessSystemUserMode());
int mainUser = mSpiedUms.getMainUserId();
- doReturn(true).when(mSpiedUms).isHeadlessSystemUserMode();
- assertThat(mSpiedUms.canAddPrivateProfile(mainUser)).isTrue();
+ // Check whether private space creation is blocked on the device
+ assumeTrue(mSpiedUms.canAddPrivateProfile(mainUser));
assertThat(mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(
PRIVATE_PROFILE_NAME, USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null)).isNotNull();
}
@Test
+ @RequiresFlagsEnabled({android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION, Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
public void testCreatePrivateProfileOnSecondaryUser_shouldNotAllowCreation() {
- mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
- android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
- mSetFlagsRule.enableFlags(Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION);
+ assumeTrue(mUms.canAddMoreUsersOfType(USER_TYPE_FULL_SECONDARY));
UserInfo user = mUms.createUserWithThrow(generateLongString(), USER_TYPE_FULL_SECONDARY, 0);
assertThat(mUms.canAddPrivateProfile(user.id)).isFalse();
assertThrows(ServiceSpecificException.class,
@@ -788,52 +803,48 @@
}
@Test
+ @RequiresFlagsEnabled({android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION, Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
public void testCreatePrivateProfileOnAutoDevices_shouldNotAllowCreation() {
- mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
- android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
- mSetFlagsRule.enableFlags(Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION);
doReturn(true).when(mMockPms).hasSystemFeature(eq(FEATURE_AUTOMOTIVE), anyInt());
int mainUser = mUms.getMainUserId();
- assertThat(mUms.canAddPrivateProfile(0)).isFalse();
+ assertThat(mUms.canAddPrivateProfile(mainUser)).isFalse();
assertThrows(ServiceSpecificException.class,
() -> mUms.createProfileForUserWithThrow(PRIVATE_PROFILE_NAME,
USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null));
}
@Test
+ @RequiresFlagsEnabled({android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION, Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
public void testCreatePrivateProfileOnTV_shouldNotAllowCreation() {
- mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
- android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
- mSetFlagsRule.enableFlags(Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION);
doReturn(true).when(mMockPms).hasSystemFeature(eq(FEATURE_LEANBACK), anyInt());
int mainUser = mUms.getMainUserId();
- assertThat(mUms.canAddPrivateProfile(0)).isFalse();
+ assertThat(mUms.canAddPrivateProfile(mainUser)).isFalse();
assertThrows(ServiceSpecificException.class,
() -> mUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null));
}
@Test
+ @RequiresFlagsEnabled({android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION, Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
public void testCreatePrivateProfileOnEmbedded_shouldNotAllowCreation() {
- mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
- android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
- mSetFlagsRule.enableFlags(Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION);
doReturn(true).when(mMockPms).hasSystemFeature(eq(FEATURE_EMBEDDED), anyInt());
int mainUser = mUms.getMainUserId();
- assertThat(mUms.canAddPrivateProfile(0)).isFalse();
+ assertThat(mUms.canAddPrivateProfile(mainUser)).isFalse();
assertThrows(ServiceSpecificException.class,
() -> mUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null));
}
@Test
+ @RequiresFlagsEnabled({android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION, Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
public void testCreatePrivateProfileOnWatch_shouldNotAllowCreation() {
- mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
- android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
- mSetFlagsRule.enableFlags(Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION);
doReturn(true).when(mMockPms).hasSystemFeature(eq(FEATURE_WATCH), anyInt());
int mainUser = mUms.getMainUserId();
- assertThat(mUms.canAddPrivateProfile(0)).isFalse();
+ assertThat(mUms.canAddPrivateProfile(mainUser)).isFalse();
assertThrows(ServiceSpecificException.class,
() -> mUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null));
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index 51c9d0a..f2b4136 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -4,58 +4,6 @@
default_applicable_licenses: ["frameworks_base_license"],
}
-filegroup {
- name: "power_stats_ravenwood_tests",
- srcs: [
- "src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java",
- "src/com/android/server/power/stats/AggregatedPowerStatsTest.java",
- "src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java",
- "src/com/android/server/power/stats/AudioPowerCalculatorTest.java",
- "src/com/android/server/power/stats/BatteryChargeCalculatorTest.java",
- "src/com/android/server/power/stats/BatteryStatsCounterTest.java",
- "src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java",
- "src/com/android/server/power/stats/BatteryStatsDualTimerTest.java",
- "src/com/android/server/power/stats/BatteryStatsDurationTimerTest.java",
- "src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java",
- "src/com/android/server/power/stats/BatteryStatsHistoryTest.java",
- "src/com/android/server/power/stats/BatteryStatsImplTest.java",
- "src/com/android/server/power/stats/BatteryStatsNoteTest.java",
- "src/com/android/server/power/stats/BatteryStatsSamplingTimerTest.java",
- "src/com/android/server/power/stats/BatteryStatsSensorTest.java",
- "src/com/android/server/power/stats/BatteryStatsServTest.java",
- "src/com/android/server/power/stats/BatteryStatsStopwatchTimerTest.java",
- "src/com/android/server/power/stats/BatteryStatsTimeBaseTest.java",
- "src/com/android/server/power/stats/BatteryStatsTimerTest.java",
- "src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java",
- "src/com/android/server/power/stats/BatteryUsageStatsTest.java",
- "src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java",
- "src/com/android/server/power/stats/CameraPowerCalculatorTest.java",
- "src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java",
- "src/com/android/server/power/stats/CpuPowerCalculatorTest.java",
- "src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java",
- "src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java",
- "src/com/android/server/power/stats/FlashlightPowerCalculatorTest.java",
- "src/com/android/server/power/stats/GnssPowerCalculatorTest.java",
- "src/com/android/server/power/stats/IdlePowerCalculatorTest.java",
- "src/com/android/server/power/stats/LongSamplingCounterArrayTest.java",
- "src/com/android/server/power/stats/LongSamplingCounterTest.java",
- "src/com/android/server/power/stats/MemoryPowerCalculatorTest.java",
- "src/com/android/server/power/stats/MultiStateStatsTest.java",
- "src/com/android/server/power/stats/PowerStatsAggregatorTest.java",
- "src/com/android/server/power/stats/PowerStatsCollectorTest.java",
- "src/com/android/server/power/stats/PowerStatsExporterTest.java",
- "src/com/android/server/power/stats/PowerStatsSchedulerTest.java",
- "src/com/android/server/power/stats/PowerStatsStoreTest.java",
- "src/com/android/server/power/stats/PowerStatsUidResolverTest.java",
- "src/com/android/server/power/stats/ScreenPowerCalculatorTest.java",
- "src/com/android/server/power/stats/SensorPowerCalculatorTest.java",
- "src/com/android/server/power/stats/UserPowerCalculatorTest.java",
- "src/com/android/server/power/stats/VideoPowerCalculatorTest.java",
- "src/com/android/server/power/stats/WakelockPowerCalculatorTest.java",
- "src/com/android/server/power/stats/WifiPowerCalculatorTest.java",
- ],
-}
-
android_test {
name: "PowerStatsTests",
@@ -79,7 +27,6 @@
"servicestests-utils",
"platform-test-annotations",
"flag-junit",
- "ravenwood-junit",
],
libs: [
@@ -112,17 +59,20 @@
name: "PowerStatsTestsRavenwood",
static_libs: [
"services.core",
- "modules-utils-binary-xml",
+ "coretests-aidl",
+ "ravenwood-junit",
+ "truth",
"androidx.annotation_annotation",
"androidx.test.rules",
- "truth",
+ "androidx.test.uiautomator_uiautomator",
+ "modules-utils-binary-xml",
+ "flag-junit",
],
srcs: [
- ":power_stats_ravenwood_tests",
-
- "src/com/android/server/power/stats/BatteryUsageStatsRule.java",
- "src/com/android/server/power/stats/MockBatteryStatsImpl.java",
- "src/com/android/server/power/stats/MockClock.java",
+ "src/com/android/server/power/stats/*.java",
+ ],
+ java_resources: [
+ "res/xml/power_profile*.xml",
],
auto_gen_config: true,
}
diff --git a/services/tests/powerstatstests/TEST_MAPPING b/services/tests/powerstatstests/TEST_MAPPING
index 6d3db1c..fb24361 100644
--- a/services/tests/powerstatstests/TEST_MAPPING
+++ b/services/tests/powerstatstests/TEST_MAPPING
@@ -12,7 +12,11 @@
"ravenwood-presubmit": [
{
"name": "PowerStatsTestsRavenwood",
- "host": true
+ "host": true,
+ "options": [
+ {"include-filter": "com.android.server.power.stats"},
+ {"exclude-annotation": "android.platform.test.annotations.DisabledOnRavenwood"}
+ ]
}
],
"postsubmit": [
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
index ca7de7c..9975190 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
@@ -20,6 +20,7 @@
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
+import android.util.SparseArray;
import android.util.Xml;
import androidx.test.filters.SmallTest;
@@ -43,6 +44,9 @@
private static final int TEST_POWER_COMPONENT = 1077;
private static final int APP_1 = 27;
private static final int APP_2 = 42;
+ private static final int COMPONENT_STATE_0 = 0;
+ private static final int COMPONENT_STATE_1 = 1;
+ private static final int COMPONENT_STATE_2 = 2;
private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
private PowerStats.Descriptor mPowerComponentDescriptor;
@@ -59,8 +63,10 @@
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
- mPowerComponentDescriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "fan", 2, 3,
- PersistableBundle.forPair("speed", "fast"));
+ SparseArray<String> stateLabels = new SparseArray<>();
+ stateLabels.put(COMPONENT_STATE_1, "one");
+ mPowerComponentDescriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "fan", 2,
+ stateLabels, 1, 3, PersistableBundle.forPair("speed", "fast"));
}
@Test
@@ -107,6 +113,9 @@
ps.stats[0] = 100;
ps.stats[1] = 987;
+ ps.stateStats.put(COMPONENT_STATE_0, new long[]{1111});
+ ps.stateStats.put(COMPONENT_STATE_1, new long[]{5000});
+
ps.uidStats.put(APP_1, new long[]{389, 0, 739});
ps.uidStats.put(APP_2, new long[]{278, 314, 628});
@@ -120,11 +129,14 @@
ps.stats[0] = 444;
ps.stats[1] = 0;
+ ps.stateStats.clear();
+ ps.stateStats.put(COMPONENT_STATE_1, new long[]{1000});
+ ps.stateStats.put(COMPONENT_STATE_2, new long[]{9000});
+
ps.uidStats.put(APP_1, new long[]{0, 0, 400});
ps.uidStats.put(APP_2, new long[]{100, 200, 300});
stats.addPowerStats(ps, 5000);
-
return stats;
}
@@ -147,6 +159,31 @@
AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
.isEqualTo(new long[]{222, 0});
+ assertThat(getStateStats(stats, COMPONENT_STATE_0,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+ .isEqualTo(new long[]{1111});
+
+ assertThat(getStateStats(stats, COMPONENT_STATE_1,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+ .isEqualTo(new long[]{5500});
+
+ assertThat(getStateStats(stats, COMPONENT_STATE_1,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
+ .isEqualTo(new long[]{500});
+
+ assertThat(getStateStats(stats, COMPONENT_STATE_2,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+ .isEqualTo(new long[]{4500});
+
+ assertThat(getStateStats(stats, COMPONENT_STATE_2,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
+ .isEqualTo(new long[]{4500});
+
assertThat(getUidDeviceStats(stats,
APP_1,
AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
@@ -191,14 +228,26 @@
}
private static long[] getDeviceStats(AggregatedPowerStats stats, int... states) {
- long[] out = new long[states.length];
- stats.getPowerComponentStats(TEST_POWER_COMPONENT).getDeviceStats(out, states);
+ PowerComponentAggregatedPowerStats powerComponentStats =
+ stats.getPowerComponentStats(TEST_POWER_COMPONENT);
+ long[] out = new long[powerComponentStats.getPowerStatsDescriptor().statsArrayLength];
+ powerComponentStats.getDeviceStats(out, states);
+ return out;
+ }
+
+ private static long[] getStateStats(AggregatedPowerStats stats, int key, int... states) {
+ PowerComponentAggregatedPowerStats powerComponentStats =
+ stats.getPowerComponentStats(TEST_POWER_COMPONENT);
+ long[] out = new long[powerComponentStats.getPowerStatsDescriptor().stateStatsArrayLength];
+ powerComponentStats.getStateStats(out, key, states);
return out;
}
private static long[] getUidDeviceStats(AggregatedPowerStats stats, int uid, int... states) {
- long[] out = new long[states.length];
- stats.getPowerComponentStats(TEST_POWER_COMPONENT).getUidStats(out, uid, states);
+ PowerComponentAggregatedPowerStats powerComponentStats =
+ stats.getPowerComponentStats(TEST_POWER_COMPONENT);
+ long[] out = new long[powerComponentStats.getPowerStatsDescriptor().uidStatsArrayLength];
+ powerComponentStats.getUidStats(out, uid, states);
return out;
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java
index 3ab1c2e..9b45ca7 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java
@@ -16,9 +16,11 @@
package com.android.server.power.stats;
-
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
import android.os.BatteryManager;
import android.os.BatteryUsageStats;
import android.platform.test.ravenwood.RavenwoodRule;
@@ -28,6 +30,7 @@
import com.android.internal.os.PowerProfile;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,6 +49,11 @@
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0);
+ @Before
+ public void setup() {
+ mStatsRule.getBatteryStats().onSystemReady(mock(Context.class));
+ }
+
@Test
public void testDischargeTotals() {
// Nominal battery capacity should be ignored
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
index 997b771..bbab0ee 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
@@ -36,6 +36,9 @@
import android.hardware.power.stats.EnergyMeasurement;
import android.hardware.power.stats.PowerEntity;
import android.hardware.power.stats.StateResidencyResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.power.PowerStatsInternal;
import android.util.IntArray;
import android.util.SparseArray;
@@ -44,9 +47,11 @@
import com.android.internal.os.Clock;
import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import java.util.Arrays;
@@ -59,19 +64,27 @@
* atest FrameworksServicesTests:BatteryExternalStatsWorkerTest
*/
@SuppressWarnings("GuardedBy")
+@android.platform.test.annotations.DisabledOnRavenwood
public class BatteryExternalStatsWorkerTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private BatteryExternalStatsWorker mBatteryExternalStatsWorker;
- private TestBatteryStatsImpl mBatteryStatsImpl;
private TestPowerStatsInternal mPowerStatsInternal;
@Before
public void setUp() {
final Context context = InstrumentationRegistry.getContext();
- mBatteryStatsImpl = new TestBatteryStatsImpl(context);
+ BatteryStatsImpl batteryStats = new BatteryStatsImpl(
+ new BatteryStatsImpl.BatteryStatsConfig.Builder().build(), Clock.SYSTEM_CLOCK,
+ new MonotonicClock(0, Clock.SYSTEM_CLOCK), null,
+ new Handler(Looper.getMainLooper()), null, null, null,
+ new PowerProfile(context, true /* forTest */), buildScalingPolicies(),
+ new PowerStatsUidResolver());
mPowerStatsInternal = new TestPowerStatsInternal();
- mBatteryExternalStatsWorker = new BatteryExternalStatsWorker(new TestInjector(context),
- mBatteryStatsImpl);
+ mBatteryExternalStatsWorker =
+ new BatteryExternalStatsWorker(new TestInjector(context), batteryStats);
}
@Test
@@ -213,24 +226,17 @@
}
}
- public class TestBatteryStatsImpl extends BatteryStatsImpl {
- public TestBatteryStatsImpl(Context context) {
- super(Clock.SYSTEM_CLOCK, null, null, null, null, null, null);
- mPowerProfile = new PowerProfile(context, true /* forTest */);
-
- SparseArray<int[]> cpusByPolicy = new SparseArray<>();
- cpusByPolicy.put(0, new int[]{0, 1, 2, 3});
- cpusByPolicy.put(4, new int[]{4, 5, 6, 7});
- SparseArray<int[]> freqsByPolicy = new SparseArray<>();
- freqsByPolicy.put(0, new int[]{300000, 1000000, 2000000});
- freqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000});
- mCpuScalingPolicies = new CpuScalingPolicies(freqsByPolicy, freqsByPolicy);
-
- initTimersAndCounters();
- }
+ private static CpuScalingPolicies buildScalingPolicies() {
+ SparseArray<int[]> cpusByPolicy = new SparseArray<>();
+ cpusByPolicy.put(0, new int[]{0, 1, 2, 3});
+ cpusByPolicy.put(4, new int[]{4, 5, 6, 7});
+ SparseArray<int[]> freqsByPolicy = new SparseArray<>();
+ freqsByPolicy.put(0, new int[]{300000, 1000000, 2000000});
+ freqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000});
+ return new CpuScalingPolicies(freqsByPolicy, freqsByPolicy);
}
- public class TestPowerStatsInternal extends PowerStatsInternal {
+ private static class TestPowerStatsInternal extends PowerStatsInternal {
private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray();
private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults = new SparseArray();
private final int mTimeSinceBoot = 0;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java
index 4d3fcb6..ad05b51 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java
@@ -18,25 +18,37 @@
import static android.os.BatteryStats.STATS_SINCE_CHARGED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import android.app.ActivityManager;
import android.os.BatteryStats;
import android.os.WorkSource;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArrayMap;
import android.view.Display;
import androidx.test.filters.SmallTest;
-import junit.framework.TestCase;
+import org.junit.Rule;
+import org.junit.Test;
/**
* Test BatteryStatsImpl onBatteryBackgroundTimeBase TimeBase.
*/
-public class BatteryStatsBackgroundStatsTest extends TestCase {
+public class BatteryStatsBackgroundStatsTest {
+
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
private static final int UID = 10500;
/** Test that BatteryStatsImpl.Uid.mOnBatteryBackgroundTimeBase works correctly. */
@SmallTest
+ @Test
public void testBgTimeBase() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
@@ -105,6 +117,7 @@
/** Test that BatteryStatsImpl.Uid.mOnBatteryScreenOffBackgroundTimeBase works correctly. */
@SmallTest
+ @Test
public void testScreenOffBgTimeBase() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
@@ -153,6 +166,7 @@
}
@SmallTest
+ @Test
public void testWifiScan() throws Exception {
final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
@@ -195,11 +209,13 @@
}
@SmallTest
+ @Test
public void testAppBluetoothScan() throws Exception {
doTestAppBluetoothScanInternal(new WorkSource(UID));
}
@SmallTest
+ @Test
public void testAppBluetoothScan_workChain() throws Exception {
WorkSource ws = new WorkSource();
ws.createWorkChain().addNode(UID, "foo");
@@ -275,6 +291,7 @@
}
@SmallTest
+ @Test
public void testJob() throws Exception {
final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
@@ -336,6 +353,7 @@
}
@SmallTest
+ @Test
public void testSyncs() throws Exception {
final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java
index 3f101a9..4dfc3fc 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java
@@ -16,20 +16,20 @@
package com.android.server.power.stats;
+import static org.junit.Assert.assertEquals;
+
import android.os.Binder;
import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderTransactionNameResolver;
-import junit.framework.TestCase;
-
+import org.junit.Rule;
import org.junit.Test;
-import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Collection;
@@ -37,9 +37,14 @@
/**
* Test cases for android.os.BatteryStats, system server Binder call stats.
*/
-@RunWith(AndroidJUnit4.class)
@SmallTest
-public class BatteryStatsBinderCallStatsTest extends TestCase {
+@android.platform.test.annotations.DisabledOnRavenwood(blockedBy = BinderCallsStats.class)
+public class BatteryStatsBinderCallStatsTest {
+
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
private static final int TRANSACTION_CODE1 = 100;
private static final int TRANSACTION_CODE2 = 101;
@@ -89,7 +94,6 @@
assertEquals(500, value.recordedCpuTimeMicros);
}
-
@Test
public void testProportionalSystemServiceUsage_noStatsForSomeMethods() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
index 6e62147..eff1b7b 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
@@ -118,7 +118,8 @@
mClocks = new MockClock();
Handler handler = new Handler(Looper.getMainLooper());
mPowerStatsUidResolver = new PowerStatsUidResolver();
- mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks, null, handler, mPowerStatsUidResolver)
+ mBatteryStatsImpl = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG,
+ mClocks, null, handler, mPowerStatsUidResolver)
.setTestCpuScalingPolicies()
.setKernelCpuUidUserSysTimeReader(mCpuUidUserSysTimeReader)
.setKernelCpuUidFreqTimeReader(mCpuUidFreqTimeReader)
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index c58c92b..e40a3e3 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -403,7 +403,7 @@
@Test
public void recordPowerStats() {
- PowerStats.Descriptor descriptor = new PowerStats.Descriptor(42, "foo", 1, 2,
+ PowerStats.Descriptor descriptor = new PowerStats.Descriptor(42, "foo", 1, null, 0, 2,
new PersistableBundle());
PowerStats powerStats = new PowerStats(descriptor);
powerStats.durationMs = 100;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java
index 7ae1117..9a64ce1 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java
@@ -25,15 +25,21 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
+import android.platform.test.ravenwood.RavenwoodRule;
+import org.junit.Rule;
import org.junit.Test;
/**
* Test BatteryStatsManager and CellularBatteryStats to ensure that valid data is being reported
* and that invalid data is not reported.
*/
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test")
public class BatteryStatsManagerTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
@Test
public void testBatteryUsageStatsDataConsistency() {
BatteryStatsManager bsm = getContext().getSystemService(BatteryStatsManager.class);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
index 07cefa9..afbe9159 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
@@ -170,8 +170,8 @@
public void testNoteStartWakeLocked_isolatedUid() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
- new Handler(Looper.getMainLooper()), uidResolver);
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG,
+ clocks, null, new Handler(Looper.getMainLooper()), uidResolver);
int pid = 10;
String name = "name";
@@ -212,8 +212,8 @@
public void testNoteStartWakeLocked_isolatedUidRace() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
- new Handler(Looper.getMainLooper()), uidResolver);
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG,
+ clocks, null, new Handler(Looper.getMainLooper()), uidResolver);
int pid = 10;
String name = "name";
@@ -256,8 +256,8 @@
public void testNoteLongPartialWakelockStart_isolatedUid() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
- new Handler(Looper.getMainLooper()), uidResolver);
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG,
+ clocks, null, new Handler(Looper.getMainLooper()), uidResolver);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -311,8 +311,8 @@
public void testNoteLongPartialWakelockStart_isolatedUidRace() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
- new Handler(Looper.getMainLooper()), uidResolver);
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG,
+ clocks, null, new Handler(Looper.getMainLooper()), uidResolver);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
index a0fb631..d29bf1a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
@@ -18,21 +18,32 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
import android.content.Context;
import android.os.BatteryManager;
+import android.platform.test.ravenwood.RavenwoodRule;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.IOException;
+import java.nio.file.Files;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BatteryStatsResetTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
private static final int BATTERY_NOMINAL_VOLTAGE_MV = 3700;
private static final int BATTERY_CAPACITY_UAH = 4_000_000;
private static final int BATTERY_CHARGE_RATE_SECONDS_PER_LEVEL = 100;
@@ -79,13 +90,11 @@
private long mBatteryChargeTimeToFullSeconds;
@Before
- public void setUp() {
- final Context context = InstrumentationRegistry.getContext();
-
+ public void setUp() throws IOException {
mMockClock = new MockClock();
- mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock, context.getFilesDir());
- mBatteryStatsImpl.onSystemReady();
-
+ mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock,
+ Files.createTempDirectory("BatteryStatsResetTest").toFile());
+ mBatteryStatsImpl.onSystemReady(mock(Context.class));
// Set up the battery state. Start off with a fully charged plugged in battery.
mBatteryStatus = BatteryManager.BATTERY_STATUS_FULL;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java
index c4561b1..3931201 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java
@@ -28,6 +28,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArraySet;
import androidx.test.InstrumentationRegistry;
@@ -38,6 +39,7 @@
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,8 +48,10 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
-@android.platform.test.annotations.IgnoreUnderRavenwood
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test")
public class BatteryStatsUserLifecycleTests {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private static final long POLL_INTERVAL_MS = 500;
private static final long USER_REMOVE_TIMEOUT_MS = 5_000;
@@ -65,6 +69,10 @@
@BeforeClass
public static void setUpOnce() {
+ if (RavenwoodRule.isOnRavenwood()) {
+ return;
+ }
+
assumeTrue(UserManager.getMaxSupportedUsers() > 1);
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 296ad0e..2d7cb22 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -24,7 +24,8 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
-import android.annotation.XmlRes;
+import android.content.Context;
+import android.content.res.Resources;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
@@ -35,9 +36,9 @@
import android.os.HandlerThread;
import android.os.UidBatteryConsumer;
import android.os.UserBatteryConsumer;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
-
-import androidx.test.InstrumentationRegistry;
+import android.util.Xml;
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
@@ -47,6 +48,7 @@
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.stubbing.Answer;
+import org.xmlpull.v1.XmlPullParser;
import java.io.File;
import java.io.IOException;
@@ -81,6 +83,7 @@
private boolean[] mSupportedStandardBuckets;
private String[] mCustomPowerComponentNames;
private Throwable mThrowable;
+ private final BatteryStatsImpl.BatteryStatsConfig.Builder mBatteryStatsConfigBuilder;
public BatteryUsageStatsRule() {
this(0);
@@ -94,6 +97,11 @@
mCpusByPolicy.put(4, new int[]{4, 5, 6, 7});
mFreqsByPolicy.put(0, new int[]{300000, 1000000, 2000000});
mFreqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000});
+ mBatteryStatsConfigBuilder = new BatteryStatsImpl.BatteryStatsConfig.Builder()
+ .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_CPU,
+ 10000)
+ .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ 10000);
}
private void initBatteryStats() {
@@ -107,7 +115,8 @@
}
clearDirectory();
}
- mBatteryStats = new MockBatteryStatsImpl(mMockClock, mHistoryDir, mHandler);
+ mBatteryStats = new MockBatteryStatsImpl(mBatteryStatsConfigBuilder.build(),
+ mMockClock, mHistoryDir, mHandler, new PowerStatsUidResolver());
mBatteryStats.setPowerProfile(mPowerProfile);
mBatteryStats.setCpuScalingPolicies(new CpuScalingPolicies(mCpusByPolicy, mFreqsByPolicy));
synchronized (mBatteryStats) {
@@ -116,8 +125,6 @@
}
mBatteryStats.informThatAllExternalStatsAreFlushed();
- mBatteryStats.onSystemReady();
-
if (mDisplayCount != -1) {
mBatteryStats.setDisplayCountLocked(mDisplayCount);
}
@@ -148,11 +155,27 @@
return this;
}
- public BatteryUsageStatsRule setTestPowerProfile(@XmlRes int xmlId) {
- mPowerProfile.forceInitForTesting(InstrumentationRegistry.getContext(), xmlId);
+ public BatteryUsageStatsRule setTestPowerProfile(String resourceName) {
+ mPowerProfile.initForTesting(resolveParser(resourceName));
return this;
}
+ public static XmlPullParser resolveParser(String resourceName) {
+ if (RavenwoodRule.isOnRavenwood()) {
+ try {
+ return Xml.resolvePullParser(BatteryUsageStatsRule.class.getClassLoader()
+ .getResourceAsStream("res/xml/" + resourceName + ".xml"));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ Context context = androidx.test.InstrumentationRegistry.getContext();
+ Resources resources = context.getResources();
+ int resId = resources.getIdentifier(resourceName, "xml", context.getPackageName());
+ return resources.getXml(resId);
+ }
+ }
+
public BatteryUsageStatsRule setCpuScalingPolicy(int policy, int[] relatedCpus,
int[] frequencies) {
if (mDefaultCpuScalingPolicy) {
@@ -265,6 +288,12 @@
return this;
}
+ public BatteryUsageStatsRule setPowerStatsThrottlePeriodMillis(int powerComponent,
+ long throttleMs) {
+ mBatteryStatsConfigBuilder.setPowerStatsThrottlePeriodMillis(powerComponent, throttleMs);
+ return this;
+ }
+
public BatteryUsageStatsRule startWithScreenOn(boolean screenOn) {
mScreenOn = screenOn;
return this;
@@ -291,23 +320,21 @@
}
private void before() {
- initBatteryStats();
HandlerThread bgThread = new HandlerThread("bg thread");
bgThread.setUncaughtExceptionHandler((thread, throwable)-> {
mThrowable = throwable;
});
bgThread.start();
mHandler = new Handler(bgThread.getLooper());
- mBatteryStats.setHandler(mHandler);
+
+ initBatteryStats();
mBatteryStats.setOnBatteryInternal(true);
mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
mBatteryStats.getOnBatteryScreenOffTimeBase().setRunning(!mScreenOn, 0, 0);
}
private void after() throws Throwable {
- if (mHandler != null) {
- waitForBackgroundThread();
- }
+ waitForBackgroundThread();
}
public void waitForBackgroundThread() throws Throwable {
@@ -316,11 +343,12 @@
}
ConditionVariable done = new ConditionVariable();
- mHandler.post(done::open);
- assertThat(done.block(10000)).isTrue();
-
- if (mThrowable != null) {
- throw mThrowable;
+ if (mHandler.post(done::open)) {
+ boolean success = done.block(5000);
+ if (mThrowable != null) {
+ throw mThrowable;
+ }
+ assertThat(success).isTrue();
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java
index 29e2f5e..e4ab227 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java
@@ -46,6 +46,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.SystemClock;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.DebugUtils;
@@ -74,9 +75,11 @@
import java.util.regex.Pattern;
@LargeTest
-@RunWith(AndroidJUnit4.class)
-@android.platform.test.annotations.IgnoreUnderRavenwood
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test")
public class BstatsCpuTimesValidationTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private static final String TAG = BstatsCpuTimesValidationTest.class.getSimpleName();
private static final String TEST_PKG = "com.android.coretests.apps.bstatstestapp";
@@ -112,10 +115,15 @@
private static boolean sCpuFreqTimesAvailable;
private static boolean sPerProcStateTimesAvailable;
- @Rule public TestName testName = new TestName();
+ @Rule(order = 1)
+ public TestName testName = new TestName();
@BeforeClass
public static void setupOnce() throws Exception {
+ if (RavenwoodRule.isOnRavenwood()) {
+ return;
+ }
+
sContext = InstrumentationRegistry.getContext();
sUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
sContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG,
@@ -127,6 +135,10 @@
@AfterClass
public static void tearDownOnce() throws Exception {
+ if (RavenwoodRule.isOnRavenwood()) {
+ return;
+ }
+
executeCmd("cmd deviceidle whitelist -" + TEST_PKG);
if (sBatteryStatsConstsUpdated) {
Settings.Global.putString(sContext.getContentResolver(),
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
index 64d5414..d51828e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
@@ -23,79 +23,134 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
-import android.content.Context;
-import android.hardware.power.stats.EnergyConsumer;
-import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
-import android.power.PowerStatsInternal;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.frameworks.powerstatstests.R;
+import com.android.internal.os.Clock;
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParserException;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
+import java.io.IOException;
+import java.util.function.IntSupplier;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class CpuPowerStatsCollectorTest {
+
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
private static final int ISOLATED_UID = 99123;
private static final int UID_1 = 42;
private static final int UID_2 = 99;
- private Context mContext;
private final MockClock mMockClock = new MockClock();
private final HandlerThread mHandlerThread = new HandlerThread("test");
+ private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
private Handler mHandler;
private PowerStats mCollectedStats;
- private PowerProfile mPowerProfile;
- @Mock
- private PowerStatsUidResolver mUidResolver;
+ private PowerProfile mPowerProfile = new PowerProfile();
@Mock
private CpuPowerStatsCollector.KernelCpuStatsReader mMockKernelCpuStatsReader;
@Mock
- private PowerStatsInternal mPowerStatsInternal;
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
private CpuScalingPolicies mCpuScalingPolicies;
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getContext();
+ private class TestInjector implements CpuPowerStatsCollector.Injector {
+ private final int mDefaultCpuPowerBrackets;
+ private final int mDefaultCpuPowerBracketsPerEnergyConsumer;
+ TestInjector(int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) {
+ mDefaultCpuPowerBrackets = defaultCpuPowerBrackets;
+ mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer;
+ }
+
+ @Override
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ public Clock getClock() {
+ return mMockClock;
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mUidResolver;
+ }
+
+ @Override
+ public CpuScalingPolicies getCpuScalingPolicies() {
+ return mCpuScalingPolicies;
+ }
+
+ @Override
+ public PowerProfile getPowerProfile() {
+ return mPowerProfile;
+ }
+
+ @Override
+ public CpuPowerStatsCollector.KernelCpuStatsReader getKernelCpuStatsReader() {
+ return mMockKernelCpuStatsReader;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> 3500;
+ }
+
+ @Override
+ public int getDefaultCpuPowerBrackets() {
+ return mDefaultCpuPowerBrackets;
+ }
+
+ @Override
+ public int getDefaultCpuPowerBracketsPerEnergyConsumer() {
+ return mDefaultCpuPowerBracketsPerEnergyConsumer;
+ }
+ };
+
+ @Before
+ public void setup() throws XmlPullParserException, IOException {
+ MockitoAnnotations.initMocks(this);
mHandlerThread.start();
mHandler = mHandlerThread.getThreadHandler();
- when(mMockKernelCpuStatsReader.nativeIsSupportedFeature()).thenReturn(true);
- when(mUidResolver.mapUid(anyInt())).thenAnswer(invocation -> {
- int uid = invocation.getArgument(0);
- if (uid == ISOLATED_UID) {
- return UID_2;
- } else {
- return uid;
- }
- });
+ when(mMockKernelCpuStatsReader.isSupportedFeature()).thenReturn(true);
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(anyInt())).thenReturn(new int[0]);
+ mUidResolver.noteIsolatedUidAdded(ISOLATED_UID, UID_2);
}
@Test
public void powerBrackets_specifiedInPowerProfile() {
- mPowerProfile = new PowerProfile(mContext);
- mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test_power_brackets);
+ mPowerProfile.initForTesting(
+ BatteryUsageStatsRule.resolveParser("power_profile_test_power_brackets"));
mCpuScalingPolicies = new CpuScalingPolicies(
new SparseArray<>() {{
put(0, new int[]{0});
@@ -114,8 +169,7 @@
@Test
public void powerBrackets_default_noEnergyConsumers() {
- mPowerProfile = new PowerProfile(mContext);
- mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mPowerProfile.initForTesting(BatteryUsageStatsRule.resolveParser("power_profile_test"));
mockCpuScalingPolicies(2);
CpuPowerStatsCollector collector = createCollector(3, 0);
@@ -134,8 +188,7 @@
@Test
public void powerBrackets_moreBracketsThanStates() {
- mPowerProfile = new PowerProfile(mContext);
- mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mPowerProfile.initForTesting(BatteryUsageStatsRule.resolveParser("power_profile_test"));
mockCpuScalingPolicies(2);
CpuPowerStatsCollector collector = createCollector(8, 0);
@@ -146,8 +199,7 @@
@Test
public void powerBrackets_energyConsumers() throws Exception {
- mPowerProfile = new PowerProfile(mContext);
- mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mPowerProfile.initForTesting(BatteryUsageStatsRule.resolveParser("power_profile_test"));
mockCpuScalingPolicies(2);
mockEnergyConsumers();
@@ -159,8 +211,7 @@
@Test
public void powerStatsDescriptor() throws Exception {
- mPowerProfile = new PowerProfile(mContext);
- mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mPowerProfile.initForTesting(BatteryUsageStatsRule.resolveParser("power_profile_test"));
mockCpuScalingPolicies(2);
mockEnergyConsumers();
@@ -170,8 +221,8 @@
assertThat(descriptor.name).isEqualTo("cpu");
assertThat(descriptor.statsArrayLength).isEqualTo(13);
assertThat(descriptor.uidStatsArrayLength).isEqualTo(5);
- CpuPowerStatsCollector.CpuStatsArrayLayout layout =
- new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ CpuPowerStatsLayout layout =
+ new CpuPowerStatsLayout();
layout.fromExtras(descriptor.extras);
long[] deviceStats = new long[descriptor.statsArrayLength];
@@ -209,8 +260,7 @@
mockEnergyConsumers();
CpuPowerStatsCollector collector = createCollector(8, 0);
- CpuPowerStatsCollector.CpuStatsArrayLayout layout =
- new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ CpuPowerStatsLayout layout = new CpuPowerStatsLayout();
layout.fromExtras(collector.getPowerStatsDescriptor().extras);
mockKernelCpuStats(new long[]{1111, 2222, 3333},
@@ -274,6 +324,45 @@
.isEqualTo(78);
}
+ @Test
+ public void isolatedUidReuse() {
+ mockCpuScalingPolicies(1);
+ mockPowerProfile();
+ mockEnergyConsumers();
+
+ CpuPowerStatsCollector collector = createCollector(8, 0);
+ CpuPowerStatsLayout layout = new CpuPowerStatsLayout();
+ layout.fromExtras(collector.getPowerStatsDescriptor().extras);
+
+ mockKernelCpuStats(new long[]{1111, 2222, 3333},
+ new SparseArray<>() {{
+ put(UID_2, new long[]{100, 150});
+ put(ISOLATED_UID, new long[]{10000, 20000});
+ }}, 0, 1234);
+
+ mMockClock.uptime = 1000;
+ collector.forceSchedule();
+ waitForIdle();
+
+ mUidResolver.noteIsolatedUidRemoved(ISOLATED_UID, UID_2);
+ mUidResolver.noteIsolatedUidAdded(ISOLATED_UID, UID_2);
+
+ mockKernelCpuStats(new long[]{5555, 4444, 3333},
+ new SparseArray<>() {{
+ put(UID_2, new long[]{100, 150});
+ put(ISOLATED_UID, new long[]{245, 528});
+ }}, 1234, 3421);
+
+ mMockClock.uptime = 2000;
+ collector.forceSchedule();
+ waitForIdle();
+
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_2), 0))
+ .isEqualTo(245);
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_2), 1))
+ .isEqualTo(528);
+ }
+
private void mockCpuScalingPolicies(int clusterCount) {
SparseArray<int[]> cpus = new SparseArray<>();
SparseArray<int[]> freqs = new SparseArray<>();
@@ -296,10 +385,9 @@
private CpuPowerStatsCollector createCollector(int defaultCpuPowerBrackets,
int defaultCpuPowerBracketsPerEnergyConsumer) {
- CpuPowerStatsCollector collector = new CpuPowerStatsCollector(mCpuScalingPolicies,
- mPowerProfile, mHandler, mMockKernelCpuStatsReader, mUidResolver,
- () -> mPowerStatsInternal, () -> 3500, 60_000, mMockClock,
- defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer);
+ CpuPowerStatsCollector collector = new CpuPowerStatsCollector(
+ new TestInjector(defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer),
+ 0);
collector.addConsumer(stats -> mCollectedStats = stats);
collector.setEnabled(true);
return collector;
@@ -307,7 +395,7 @@
private void mockKernelCpuStats(long[] deviceStats, SparseArray<long[]> uidToCpuStats,
long expectedLastUpdateTimestampMs, long newLastUpdateTimestampMs) {
- when(mMockKernelCpuStatsReader.nativeReadCpuStats(
+ when(mMockKernelCpuStatsReader.readCpuStats(
any(CpuPowerStatsCollector.KernelCpuStatsCallback.class),
any(int[].class), anyLong(), any(long[].class), any(long[].class)))
.thenAnswer(invocation -> {
@@ -335,63 +423,18 @@
});
}
- @SuppressWarnings("unchecked")
- private void mockEnergyConsumers() throws Exception {
- when(mPowerStatsInternal.getEnergyConsumerInfo())
- .thenReturn(new EnergyConsumer[]{
- new EnergyConsumer() {{
- id = 1;
- type = EnergyConsumerType.CPU_CLUSTER;
- ordinal = 0;
- name = "CPU0";
- }},
- new EnergyConsumer() {{
- id = 2;
- type = EnergyConsumerType.CPU_CLUSTER;
- ordinal = 1;
- name = "CPU4";
- }},
- new EnergyConsumer() {{
- id = 3;
- type = EnergyConsumerType.BLUETOOTH;
- name = "BT";
- }},
- });
-
- CompletableFuture<EnergyConsumerResult[]> future1 = mock(CompletableFuture.class);
- when(future1.get(anyLong(), any(TimeUnit.class)))
- .thenReturn(new EnergyConsumerResult[]{
- new EnergyConsumerResult() {{
- id = 1;
- energyUWs = 1000;
- }},
- new EnergyConsumerResult() {{
- id = 2;
- energyUWs = 2000;
- }}
- });
-
- CompletableFuture<EnergyConsumerResult[]> future2 = mock(CompletableFuture.class);
- when(future2.get(anyLong(), any(TimeUnit.class)))
- .thenReturn(new EnergyConsumerResult[]{
- new EnergyConsumerResult() {{
- id = 1;
- energyUWs = 1500;
- }},
- new EnergyConsumerResult() {{
- id = 2;
- energyUWs = 2700;
- }}
- });
-
- when(mPowerStatsInternal.getEnergyConsumedAsync(eq(new int[]{1, 2})))
- .thenReturn(future1)
- .thenReturn(future2);
+ private void mockEnergyConsumers() {
+ reset(mConsumedEnergyRetriever);
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER))
+ .thenReturn(new int[]{1, 2});
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{1, 2})))
+ .thenReturn(new long[]{1000, 2000})
+ .thenReturn(new long[]{1500, 2700});
}
private static int[] getScalingStepToPowerBracketMap(CpuPowerStatsCollector collector) {
- CpuPowerStatsCollector.CpuStatsArrayLayout layout =
- new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ CpuPowerStatsLayout layout =
+ new CpuPowerStatsLayout();
layout.fromExtras(collector.getPowerStatsDescriptor().extras);
return layout.getScalingStepToPowerBracketMap();
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
index cbce7e8..70c40f5 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
@@ -28,6 +28,8 @@
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.provider.DeviceConfig;
import androidx.test.InstrumentationRegistry;
@@ -52,11 +54,15 @@
@RunWith(AndroidJUnit4.class)
@LargeTest
-@android.platform.test.annotations.IgnoreUnderRavenwood
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test")
public class CpuPowerStatsCollectorValidationTest {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Rule(order = 1)
+ public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood()
+ ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
+ : DeviceFlagsValueProvider.createCheckFlagsRule();
private static final int WORK_DURATION_MS = 2000;
private static final String TEST_PKG = "com.android.coretests.apps.bstatstestapp";
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
similarity index 96%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
index 5c0e268..6b5da81 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
@@ -30,6 +30,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
@@ -55,7 +56,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class CpuAggregatedPowerStatsProcessorTest {
+public class CpuPowerStatsProcessorTest {
@Rule(order = 0)
public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
.setProvideMainThread(true)
@@ -77,7 +78,7 @@
.setCpuPowerBracket(2, 0, 2);
private AggregatedPowerStatsConfig.PowerComponent mConfig;
- private CpuAggregatedPowerStatsProcessor mProcessor;
+ private CpuPowerStatsProcessor mProcessor;
private MockPowerComponentAggregatedPowerStats mStats;
@Before
@@ -86,7 +87,7 @@
.trackDeviceStates(STATE_POWER, STATE_SCREEN)
.trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE);
- mProcessor = new CpuAggregatedPowerStatsProcessor(
+ mProcessor = new CpuPowerStatsProcessor(
mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies());
}
@@ -197,7 +198,7 @@
private static class MockPowerComponentAggregatedPowerStats extends
PowerComponentAggregatedPowerStats {
- private final CpuPowerStatsCollector.CpuStatsArrayLayout mStatsLayout;
+ private final CpuPowerStatsLayout mStatsLayout;
private final PowerStats.Descriptor mDescriptor;
private HashMap<String, long[]> mDeviceStats = new HashMap<>();
private HashMap<String, long[]> mUidStats = new HashMap<>();
@@ -207,8 +208,8 @@
MockPowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config,
boolean useEnergyConsumers) {
- super(config);
- mStatsLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ super(new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+ mStatsLayout = new CpuPowerStatsLayout();
mStatsLayout.addDeviceSectionCpuTimeByScalingStep(3);
mStatsLayout.addDeviceSectionCpuTimeByCluster(2);
mStatsLayout.addDeviceSectionUsageDuration();
@@ -222,8 +223,8 @@
PersistableBundle extras = new PersistableBundle();
mStatsLayout.toExtras(extras);
mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
- mStatsLayout.getDeviceStatsArrayLength(), mStatsLayout.getUidStatsArrayLength(),
- extras);
+ mStatsLayout.getDeviceStatsArrayLength(), null, 0,
+ mStatsLayout.getUidStatsArrayLength(), extras);
}
@Override
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java
index e023866..f035465 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java
@@ -16,16 +16,26 @@
package com.android.server.power.stats;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.ravenwood.RavenwoodRule;
import android.system.suspend.internal.WakeLockInfo;
import androidx.test.filters.SmallTest;
-import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
import java.nio.charset.Charset;
-@android.platform.test.annotations.IgnoreUnderRavenwood
-public class KernelWakelockReaderTest extends TestCase {
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Kernel dependency")
+public class KernelWakelockReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
/**
* Helper class that builds the mock Kernel module file /d/wakeup_sources.
*/
@@ -105,14 +115,14 @@
private KernelWakelockReader mReader;
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
mReader = new KernelWakelockReader();
}
// ------------------------- Legacy Wakelock Stats Test ------------------------
@SmallTest
+ @Test
public void testParseEmptyFile() throws Exception {
KernelWakelockStats staleStats = mReader.parseProcWakelocks(new byte[0], 0, true,
new KernelWakelockStats());
@@ -121,6 +131,7 @@
}
@SmallTest
+ @Test
public void testOnlyHeader() throws Exception {
byte[] buffer = new ProcFileBuilder().getBytes();
@@ -131,6 +142,7 @@
}
@SmallTest
+ @Test
public void testOneWakelock() throws Exception {
byte[] buffer = new ProcFileBuilder()
.addLine("Wakelock", 34, 123, 456) // Milliseconds
@@ -150,6 +162,7 @@
}
@SmallTest
+ @Test
public void testTwoWakelocks() throws Exception {
byte[] buffer = new ProcFileBuilder()
.addLine("Wakelock", 1, 10)
@@ -166,6 +179,7 @@
}
@SmallTest
+ @Test
public void testDuplicateWakelocksAccumulate() throws Exception {
byte[] buffer = new ProcFileBuilder()
.addLine("Wakelock", 1, 10) // Milliseconds
@@ -184,6 +198,7 @@
}
@SmallTest
+ @Test
public void testWakelocksBecomeStale() throws Exception {
KernelWakelockStats staleStats = new KernelWakelockStats();
@@ -209,6 +224,7 @@
// -------------------- SystemSuspend Wakelock Stats Test -------------------
@SmallTest
+ @Test
public void testEmptyWakeLockInfoList() {
KernelWakelockStats staleStats = mReader.updateWakelockStats(new WakeLockInfo[0],
new KernelWakelockStats());
@@ -217,6 +233,7 @@
}
@SmallTest
+ @Test
public void testOneWakeLockInfo() {
WakeLockInfo[] wlStats = new WakeLockInfo[1];
wlStats[0] = createWakeLockInfo("WakeLock", 20, 1000, 500); // Milliseconds
@@ -235,6 +252,7 @@
}
@SmallTest
+ @Test
public void testTwoWakeLockInfos() {
WakeLockInfo[] wlStats = new WakeLockInfo[2];
wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds
@@ -258,6 +276,7 @@
}
@SmallTest
+ @Test
public void testWakeLockInfosBecomeStale() {
WakeLockInfo[] wlStats = new WakeLockInfo[1];
wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds
@@ -288,6 +307,7 @@
// -------------------- Aggregate Wakelock Stats Tests --------------------
@SmallTest
+ @Test
public void testAggregateStatsEmpty() throws Exception {
KernelWakelockStats staleStats = new KernelWakelockStats();
@@ -300,6 +320,7 @@
}
@SmallTest
+ @Test
public void testAggregateStatsNoNativeWakelocks() throws Exception {
KernelWakelockStats staleStats = new KernelWakelockStats();
@@ -320,6 +341,7 @@
}
@SmallTest
+ @Test
public void testAggregateStatsNoKernelWakelocks() throws Exception {
KernelWakelockStats staleStats = new KernelWakelockStats();
@@ -339,6 +361,7 @@
}
@SmallTest
+ @Test
public void testAggregateStatsBothKernelAndNativeWakelocks() throws Exception {
KernelWakelockStats staleStats = new KernelWakelockStats();
@@ -364,6 +387,7 @@
}
@SmallTest
+ @Test
public void testAggregateStatsUpdate() throws Exception {
KernelWakelockStats staleStats = new KernelWakelockStats();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
index 888a168..9b810bc 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.app.usage.NetworkStatsManager;
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
@@ -34,6 +35,7 @@
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.telephony.AccessNetworkConstants;
import android.telephony.ActivityStatsTechSpecificInfo;
import android.telephony.CellSignalStrength;
@@ -46,8 +48,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.frameworks.powerstatstests.R;
-
import com.google.common.collect.Range;
import org.junit.Rule;
@@ -56,23 +56,29 @@
import org.mockito.Mock;
import java.util.ArrayList;
+import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
@SuppressWarnings("GuardedBy")
public class MobileRadioPowerCalculatorTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
private static final double PRECISION = 0.00001;
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
@Mock
NetworkStatsManager mNetworkStatsManager;
- @Rule
+ @Rule(order = 1)
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
@Test
public void testCounterBasedModel() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_modem_calculator)
+ mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator")
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
@@ -126,10 +132,10 @@
stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665);
// Note application network activity
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100))
- .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0,
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 300, 10, 111));
mStatsRule.setNetworkStats(networkStats);
@@ -192,7 +198,7 @@
@Test
public void testCounterBasedModel_multipleDefinedRat() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_modem_calculator_multiactive)
+ mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator_multiactive")
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
@@ -246,10 +252,10 @@
stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665);
// Note application network activity
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100))
- .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0,
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 300, 10, 111));
mStatsRule.setNetworkStats(networkStats);
@@ -349,7 +355,7 @@
@Test
public void testCounterBasedModel_legacyPowerProfile() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem)
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
@@ -403,10 +409,10 @@
stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665);
// Note application network activity
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100))
- .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0,
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 300, 10, 111));
mStatsRule.setNetworkStats(networkStats);
@@ -469,7 +475,7 @@
@Test
public void testTimerBasedModel_byProcessState() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem)
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
BatteryStatsImpl.Uid uid = stats.getUidStatsLocked(APP_UID);
@@ -521,8 +527,8 @@
stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665);
// Note application network activity
- mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ mStatsRule.setNetworkStats(mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100)));
stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000,
@@ -531,8 +537,8 @@
uid.setProcessStateForTest(
BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
- mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ mStatsRule.setNetworkStats(mockNetworkStats(12000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 250, 2000, 80, 200)));
stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000,
@@ -586,7 +592,7 @@
@Test
public void testMeasuredEnergyBasedModel_mobileRadioActiveTimeModel() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem)
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
.setPerUidModemModel(
BatteryStatsImpl.PER_UID_MODEM_POWER_MODEL_MOBILE_RADIO_ACTIVE_TIME)
.initMeasuredEnergyStatsLocked();
@@ -619,8 +625,8 @@
stats.notePhoneOnLocked(9800, 9800);
// Note application network activity
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100));
mStatsRule.setNetworkStats(networkStats);
@@ -662,11 +668,9 @@
.isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
-
-
@Test
public void testMeasuredEnergyBasedModel_modemActivityInfoRxTxModel() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_modem_calculator_multiactive)
+ mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator_multiactive")
.setPerUidModemModel(
BatteryStatsImpl.PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX)
.initMeasuredEnergyStatsLocked();
@@ -728,10 +732,10 @@
stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665);
// Note application network activity
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 300, 10, 100))
- .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0,
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 300, 10, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 2000, 30, 111));
mStatsRule.setNetworkStats(networkStats);
@@ -850,7 +854,7 @@
@Test
public void testMeasuredEnergyBasedModel_modemActivityInfoRxTxModel_legacyPowerProfile() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem)
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
.setPerUidModemModel(
BatteryStatsImpl.PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX)
.initMeasuredEnergyStatsLocked();
@@ -908,8 +912,8 @@
stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665);
// Note application network activity
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100));
mStatsRule.setNetworkStats(networkStats);
@@ -957,7 +961,7 @@
@Test
public void testMeasuredEnergyBasedModel_byProcessState() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem)
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
BatteryStatsImpl.Uid uid = stats.getUidStatsLocked(APP_UID);
@@ -988,8 +992,8 @@
new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
// Note application network activity
- mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ mStatsRule.setNetworkStats(mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100)));
stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000, mNetworkStatsManager);
@@ -997,8 +1001,8 @@
uid.setProcessStateForTest(
BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
- mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ mStatsRule.setNetworkStats(mockNetworkStats(12000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 250, 2000, 80, 200)));
stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000, mNetworkStatsManager);
@@ -1047,4 +1051,40 @@
final ModemActivityInfo emptyMai = new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L);
stats.noteModemControllerActivity(emptyMai, 0, 0, 0, mNetworkStatsManager);
}
+
+ private NetworkStats mockNetworkStats(int elapsedTime, int initialSize,
+ NetworkStats.Entry... entries) {
+ NetworkStats stats;
+ if (RavenwoodRule.isOnRavenwood()) {
+ stats = mock(NetworkStats.class);
+ when(stats.iterator()).thenAnswer(inv -> List.of(entries).iterator());
+ } else {
+ stats = new NetworkStats(elapsedTime, initialSize);
+ for (NetworkStats.Entry entry : entries) {
+ stats = stats.addEntry(entry);
+ }
+ }
+ return stats;
+ }
+
+ private static NetworkStats.Entry mockNetworkStatsEntry(@Nullable String iface, int uid,
+ int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes,
+ long rxPackets, long txBytes, long txPackets, long operations) {
+ if (RavenwoodRule.isOnRavenwood()) {
+ NetworkStats.Entry entry = mock(NetworkStats.Entry.class);
+ when(entry.getUid()).thenReturn(uid);
+ when(entry.getMetered()).thenReturn(metered);
+ when(entry.getRoaming()).thenReturn(roaming);
+ when(entry.getDefaultNetwork()).thenReturn(defaultNetwork);
+ when(entry.getRxBytes()).thenReturn(rxBytes);
+ when(entry.getRxPackets()).thenReturn(rxPackets);
+ when(entry.getTxBytes()).thenReturn(txBytes);
+ when(entry.getTxPackets()).thenReturn(txPackets);
+ when(entry.getOperations()).thenReturn(operations);
+ return entry;
+ } else {
+ return new NetworkStats.Entry(iface, uid, set, tag, metered,
+ roaming, defaultNetwork, rxBytes, rxPackets, txBytes, txPackets, operations);
+ }
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
new file mode 100644
index 0000000..f93c4da
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2021 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.power.stats;
+
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.OutcomeReceiver;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ActivityStatsTechSpecificInfo;
+import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+public class MobileRadioPowerStatsCollectorTest {
+ private static final int APP_UID1 = 42;
+ private static final int APP_UID2 = 24;
+ private static final int APP_UID3 = 44;
+ private static final int ISOLATED_UID = 99123;
+
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ @Rule(order = 1)
+ public final BatteryUsageStatsRule mStatsRule =
+ new BatteryUsageStatsRule().setPowerStatsThrottlePeriodMillis(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 10000);
+
+ private MockBatteryStatsImpl mBatteryStats;
+
+ private final MockClock mClock = mStatsRule.getMockClock();
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private TelephonyManager mTelephony;
+ @Mock
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ @Mock
+ private Supplier<NetworkStats> mNetworkStatsSupplier;
+ @Mock
+ private PowerStatsUidResolver mPowerStatsUidResolver;
+ @Mock
+ private LongSupplier mCallDurationSupplier;
+ @Mock
+ private LongSupplier mScanDurationSupplier;
+
+ private final List<PowerStats> mRecordedPowerStats = new ArrayList<>();
+
+ private MobileRadioPowerStatsCollector.Injector mInjector =
+ new MobileRadioPowerStatsCollector.Injector() {
+ @Override
+ public Handler getHandler() {
+ return mStatsRule.getHandler();
+ }
+
+ @Override
+ public Clock getClock() {
+ return mStatsRule.getMockClock();
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mPowerStatsUidResolver;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> 3500;
+ }
+
+ @Override
+ public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
+ return mNetworkStatsSupplier;
+ }
+
+ @Override
+ public TelephonyManager getTelephonyManager() {
+ return mTelephony;
+ }
+
+ @Override
+ public LongSupplier getCallDurationSupplier() {
+ return mCallDurationSupplier;
+ }
+
+ @Override
+ public LongSupplier getPhoneSignalScanDurationSupplier() {
+ return mScanDurationSupplier;
+ }
+ };
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+ when(mPowerStatsUidResolver.mapUid(anyInt())).thenAnswer(invocation -> {
+ int uid = invocation.getArgument(0);
+ if (uid == ISOLATED_UID) {
+ return APP_UID2;
+ } else {
+ return uid;
+ }
+ });
+ mBatteryStats = mStatsRule.getBatteryStats();
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void triggering() throws Throwable {
+ PowerStatsCollector collector = mBatteryStats.getPowerStatsCollector(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+ collector.addConsumer(mRecordedPowerStats::add);
+
+ mBatteryStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ true);
+
+ mockModemActivityInfo(1000, 2000, 3000, 600, new int[]{100, 200, 300, 400, 500});
+
+ // This should trigger a sample collection
+ mBatteryStats.onSystemReady(mContext);
+
+ mStatsRule.waitForBackgroundThread();
+ assertThat(mRecordedPowerStats).hasSize(1);
+
+ mRecordedPowerStats.clear();
+ mStatsRule.setTime(20000, 20000);
+ mBatteryStats.notePhoneOnLocked(mClock.realtime, mClock.uptime);
+ mStatsRule.waitForBackgroundThread();
+ assertThat(mRecordedPowerStats).hasSize(1);
+
+ mRecordedPowerStats.clear();
+ mStatsRule.setTime(40000, 40000);
+ mBatteryStats.notePhoneOffLocked(mClock.realtime, mClock.uptime);
+ mStatsRule.waitForBackgroundThread();
+ assertThat(mRecordedPowerStats).hasSize(1);
+
+ mRecordedPowerStats.clear();
+ mStatsRule.setTime(45000, 55000);
+ mBatteryStats.noteMobileRadioPowerStateLocked(
+ DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, 0, APP_UID1, mClock.realtime,
+ mClock.uptime);
+ mStatsRule.setTime(50001, 50001);
+ // Elapsed time under the throttling threshold - shouldn't trigger stats collection
+ mBatteryStats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+ 0, APP_UID1, mClock.realtime, mClock.uptime);
+ mStatsRule.waitForBackgroundThread();
+ assertThat(mRecordedPowerStats).hasSize(1);
+
+ mRecordedPowerStats.clear();
+ mStatsRule.setTime(50002, 50002);
+ mBatteryStats.noteMobileRadioPowerStateLocked(
+ DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, 0, APP_UID1, mClock.realtime,
+ mClock.uptime);
+ mStatsRule.setTime(55000, 50000);
+ // Elapsed time under the throttling threshold - shouldn't trigger stats collection
+ mBatteryStats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+ 0, APP_UID1, mClock.realtime, mClock.uptime);
+ mStatsRule.waitForBackgroundThread();
+ assertThat(mRecordedPowerStats).isEmpty();
+ }
+
+ @Test
+ public void collectStats() throws Throwable {
+ PowerStats powerStats = collectPowerStats(true);
+ assertThat(powerStats.durationMs).isEqualTo(100);
+
+ PowerStats.Descriptor descriptor = powerStats.descriptor;
+ MobileRadioPowerStatsLayout layout =
+ new MobileRadioPowerStatsLayout(descriptor);
+ assertThat(layout.getDeviceSleepTime(powerStats.stats)).isEqualTo(200);
+ assertThat(layout.getDeviceIdleTime(powerStats.stats)).isEqualTo(300);
+ assertThat(layout.getDeviceCallTime(powerStats.stats)).isEqualTo(40000);
+ assertThat(layout.getDeviceScanTime(powerStats.stats)).isEqualTo(60000);
+ assertThat(layout.getConsumedEnergy(powerStats.stats, 0))
+ .isEqualTo((64321 - 10000) * 1000 / 3500);
+
+ assertThat(powerStats.stateStats.size()).isEqualTo(2);
+ long[] state1 = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey(
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR,
+ ServiceState.FREQUENCY_RANGE_MMWAVE
+ ));
+ assertThat(layout.getStateRxTime(state1)).isEqualTo(6000);
+ assertThat(layout.getStateTxTime(state1, 0)).isEqualTo(1000);
+ assertThat(layout.getStateTxTime(state1, 1)).isEqualTo(2000);
+ assertThat(layout.getStateTxTime(state1, 2)).isEqualTo(3000);
+ assertThat(layout.getStateTxTime(state1, 3)).isEqualTo(4000);
+ assertThat(layout.getStateTxTime(state1, 4)).isEqualTo(5000);
+
+ long[] state2 = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey(
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ ServiceState.FREQUENCY_RANGE_LOW
+ ));
+ assertThat(layout.getStateRxTime(state2)).isEqualTo(7000);
+ assertThat(layout.getStateTxTime(state2, 0)).isEqualTo(8000);
+ assertThat(layout.getStateTxTime(state2, 1)).isEqualTo(9000);
+ assertThat(layout.getStateTxTime(state2, 2)).isEqualTo(1000);
+ assertThat(layout.getStateTxTime(state2, 3)).isEqualTo(2000);
+ assertThat(layout.getStateTxTime(state2, 4)).isEqualTo(3000);
+
+ assertThat(powerStats.uidStats.size()).isEqualTo(2);
+ long[] actual1 = powerStats.uidStats.get(APP_UID1);
+ assertThat(layout.getUidRxBytes(actual1)).isEqualTo(1000);
+ assertThat(layout.getUidTxBytes(actual1)).isEqualTo(2000);
+ assertThat(layout.getUidRxPackets(actual1)).isEqualTo(100);
+ assertThat(layout.getUidTxPackets(actual1)).isEqualTo(200);
+
+ // Combines APP_UID2 and ISOLATED_UID
+ long[] actual2 = powerStats.uidStats.get(APP_UID2);
+ assertThat(layout.getUidRxBytes(actual2)).isEqualTo(6000);
+ assertThat(layout.getUidTxBytes(actual2)).isEqualTo(3000);
+ assertThat(layout.getUidRxPackets(actual2)).isEqualTo(60);
+ assertThat(layout.getUidTxPackets(actual2)).isEqualTo(30);
+
+ assertThat(powerStats.uidStats.get(ISOLATED_UID)).isNull();
+ assertThat(powerStats.uidStats.get(APP_UID3)).isNull();
+ }
+
+ @Test
+ public void collectStats_noPerNetworkTypeData() throws Throwable {
+ PowerStats powerStats = collectPowerStats(false);
+ assertThat(powerStats.durationMs).isEqualTo(100);
+
+ PowerStats.Descriptor descriptor = powerStats.descriptor;
+ MobileRadioPowerStatsLayout layout =
+ new MobileRadioPowerStatsLayout(descriptor);
+ assertThat(layout.getDeviceSleepTime(powerStats.stats)).isEqualTo(200);
+ assertThat(layout.getDeviceIdleTime(powerStats.stats)).isEqualTo(300);
+ assertThat(layout.getConsumedEnergy(powerStats.stats, 0))
+ .isEqualTo((64321 - 10000) * 1000 / 3500);
+
+ assertThat(powerStats.stateStats.size()).isEqualTo(1);
+ long[] stateStats = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey(
+ AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN
+ ));
+ assertThat(layout.getStateRxTime(stateStats)).isEqualTo(6000);
+ assertThat(layout.getStateTxTime(stateStats, 0)).isEqualTo(1000);
+ assertThat(layout.getStateTxTime(stateStats, 1)).isEqualTo(2000);
+ assertThat(layout.getStateTxTime(stateStats, 2)).isEqualTo(3000);
+ assertThat(layout.getStateTxTime(stateStats, 3)).isEqualTo(4000);
+ assertThat(layout.getStateTxTime(stateStats, 4)).isEqualTo(5000);
+
+ assertThat(powerStats.uidStats.size()).isEqualTo(2);
+ long[] actual1 = powerStats.uidStats.get(APP_UID1);
+ assertThat(layout.getUidRxBytes(actual1)).isEqualTo(1000);
+ assertThat(layout.getUidTxBytes(actual1)).isEqualTo(2000);
+ assertThat(layout.getUidRxPackets(actual1)).isEqualTo(100);
+ assertThat(layout.getUidTxPackets(actual1)).isEqualTo(200);
+
+ // Combines APP_UID2 and ISOLATED_UID
+ long[] actual2 = powerStats.uidStats.get(APP_UID2);
+ assertThat(layout.getUidRxBytes(actual2)).isEqualTo(6000);
+ assertThat(layout.getUidTxBytes(actual2)).isEqualTo(3000);
+ assertThat(layout.getUidRxPackets(actual2)).isEqualTo(60);
+ assertThat(layout.getUidTxPackets(actual2)).isEqualTo(30);
+
+ assertThat(powerStats.uidStats.get(ISOLATED_UID)).isNull();
+ assertThat(powerStats.uidStats.get(APP_UID3)).isNull();
+ }
+
+ @Test
+ public void dump() throws Throwable {
+ PowerStats powerStats = collectPowerStats(true);
+ StringWriter sw = new StringWriter();
+ IndentingPrintWriter pw = new IndentingPrintWriter(sw);
+ powerStats.dump(pw);
+ pw.flush();
+ String dump = sw.toString();
+ assertThat(dump).contains("duration=100");
+ assertThat(dump).contains(
+ "stats=[200, 300, 60000, 40000, " + ((64321 - 10000) * 1000 / 3500) + ", 0, 0, 0]");
+ assertThat(dump).contains("state LTE: [7000, 8000, 9000, 1000, 2000, 3000]");
+ assertThat(dump).contains("state NR MMWAVE: [6000, 1000, 2000, 3000, 4000, 5000]");
+ assertThat(dump).contains("UID 24: [6000, 3000, 60, 30, 0]");
+ assertThat(dump).contains("UID 42: [1000, 2000, 100, 200, 0]");
+ }
+
+ private PowerStats collectPowerStats(boolean perNetworkTypeData) throws Throwable {
+ MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+ collector.setEnabled(true);
+
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(
+ EnergyConsumerType.MOBILE_RADIO)).thenReturn(new int[]{777});
+
+ if (perNetworkTypeData) {
+ mockModemActivityInfo(1000, 2000, 3000,
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_MMWAVE,
+ 600, new int[]{100, 200, 300, 400, 500},
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ ServiceState.FREQUENCY_RANGE_LOW,
+ 700, new int[]{800, 900, 100, 200, 300});
+ } else {
+ mockModemActivityInfo(1000, 2000, 3000, 600, new int[]{100, 200, 300, 400, 500});
+ }
+ mockNetworkStats(1000,
+ 4321, 321, 1234, 23,
+ 4000, 40, 2000, 20);
+
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
+ .thenReturn(new long[]{10000});
+
+ when(mCallDurationSupplier.getAsLong()).thenReturn(10000L);
+ when(mScanDurationSupplier.getAsLong()).thenReturn(20000L);
+
+ collector.collectStats();
+
+ if (perNetworkTypeData) {
+ mockModemActivityInfo(1100, 2200, 3300,
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_MMWAVE,
+ 6600, new int[]{1100, 2200, 3300, 4400, 5500},
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ ServiceState.FREQUENCY_RANGE_LOW,
+ 7700, new int[]{8800, 9900, 1100, 2200, 3300});
+ } else {
+ mockModemActivityInfo(1100, 2200, 3300, 6600, new int[]{1100, 2200, 3300, 4400, 5500});
+ }
+ mockNetworkStats(1100,
+ 5321, 421, 3234, 223,
+ 8000, 80, 4000, 40);
+
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
+ .thenReturn(new long[]{64321});
+ when(mCallDurationSupplier.getAsLong()).thenReturn(50000L);
+ when(mScanDurationSupplier.getAsLong()).thenReturn(80000L);
+
+ mStatsRule.setTime(20000, 20000);
+ return collector.collectStats();
+ }
+
+ private void mockModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
+ int networkType1, int freqRange1, int rxTimeMs1, @NonNull int[] txTimeMs1,
+ int networkType2, int freqRange2, int rxTimeMs2, @NonNull int[] txTimeMs2) {
+ ModemActivityInfo info = new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs,
+ new ActivityStatsTechSpecificInfo[]{
+ new ActivityStatsTechSpecificInfo(networkType1, freqRange1, txTimeMs1,
+ rxTimeMs1),
+ new ActivityStatsTechSpecificInfo(networkType2, freqRange2, txTimeMs2,
+ rxTimeMs2)});
+ doAnswer(invocation -> {
+ OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
+ receiver = invocation.getArgument(1);
+ receiver.onResult(info);
+ return null;
+ }).when(mTelephony).requestModemActivityInfo(any(), any());
+ }
+
+ private void mockModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
+ int rxTimeMs, @NonNull int[] txTimeMs) {
+ ModemActivityInfo info = new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs, txTimeMs,
+ rxTimeMs);
+ doAnswer(invocation -> {
+ OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
+ receiver = invocation.getArgument(1);
+ receiver.onResult(info);
+ return null;
+ }).when(mTelephony).requestModemActivityInfo(any(), any());
+ }
+
+ private void mockNetworkStats(long elapsedRealtime,
+ long rxBytes1, long rxPackets1, long txBytes1, long txPackets1,
+ long rxBytes2, long rxPackets2, long txBytes2, long txPackets2) {
+ NetworkStats stats;
+ if (RavenwoodRule.isOnRavenwood()) {
+ stats = mock(NetworkStats.class);
+ List<NetworkStats.Entry> entries = List.of(
+ mockNetworkStatsEntry(APP_UID1, rxBytes1, rxPackets1, txBytes1, txPackets1),
+ mockNetworkStatsEntry(APP_UID2, rxBytes2, rxPackets2, txBytes2, txPackets2),
+ mockNetworkStatsEntry(ISOLATED_UID, rxBytes2 / 2, rxPackets2 / 2, txBytes2 / 2,
+ txPackets2 / 2),
+ mockNetworkStatsEntry(APP_UID3, 314, 281, 314, 281));
+ when(stats.iterator()).thenAnswer(inv -> entries.iterator());
+ } else {
+ stats = new NetworkStats(elapsedRealtime, 1)
+ .addEntry(new NetworkStats.Entry("mobile", APP_UID1, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes1, rxPackets1,
+ txBytes1, txPackets1, 100))
+ .addEntry(new NetworkStats.Entry("mobile", APP_UID2, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes2, rxPackets2,
+ txBytes2, txPackets2, 111))
+ .addEntry(new NetworkStats.Entry("mobile", ISOLATED_UID, 0, 0, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes2 / 2, rxPackets2 / 2,
+ txBytes2 / 2, txPackets2 / 2, 111))
+ .addEntry(new NetworkStats.Entry("mobile", APP_UID3, 0, 0, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 314, 281, 314, 281, 111));
+ }
+ when(mNetworkStatsSupplier.get()).thenReturn(stats);
+ }
+
+ private static NetworkStats.Entry mockNetworkStatsEntry(int uid, long rxBytes, long rxPackets,
+ long txBytes, long txPackets) {
+ NetworkStats.Entry entry = mock(NetworkStats.Entry.class);
+ when(entry.getUid()).thenReturn(uid);
+ when(entry.getMetered()).thenReturn(METERED_NO);
+ when(entry.getRoaming()).thenReturn(ROAMING_NO);
+ when(entry.getDefaultNetwork()).thenReturn(DEFAULT_NETWORK_NO);
+ when(entry.getRxBytes()).thenReturn(rxBytes);
+ when(entry.getRxPackets()).thenReturn(rxPackets);
+ when(entry.getTxBytes()).thenReturn(txBytes);
+ when(entry.getTxPackets()).thenReturn(txPackets);
+ when(entry.getOperations()).thenReturn(100L);
+ return entry;
+ }
+
+ @Test
+ public void networkTypeConstants() throws Throwable {
+ Class<AccessNetworkConstants.AccessNetworkType> clazz =
+ AccessNetworkConstants.AccessNetworkType.class;
+ for (Field field : clazz.getDeclaredFields()) {
+ final int modifiers = field.getModifiers();
+ if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
+ && field.getType().equals(int.class)) {
+ boolean found = false;
+ int value = field.getInt(null);
+ for (int i = 0; i < MobileRadioPowerStatsCollector.NETWORK_TYPES.length; i++) {
+ if (MobileRadioPowerStatsCollector.NETWORK_TYPES[i] == value) {
+ found = true;
+ break;
+ }
+ }
+ assertWithMessage("New network type, " + field.getName() + " not represented in "
+ + MobileRadioPowerStatsCollector.class).that(found).isTrue();
+ }
+ }
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
new file mode 100644
index 0000000..4ac7ad8
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats;
+
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.Handler;
+import android.os.OutcomeReceiver;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.telephony.ModemActivityInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+public class MobileRadioPowerStatsProcessorTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ private static final double PRECISION = 0.00001;
+ private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+ private static final int MOBILE_RADIO_ENERGY_CONSUMER_ID = 1;
+ private static final int VOLTAGE_MV = 3500;
+
+ @Rule(order = 1)
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+ @Mock
+ private Context mContext;
+ @Mock
+ private PowerStatsUidResolver mPowerStatsUidResolver;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ @Mock
+ private Supplier<NetworkStats> mNetworkStatsSupplier;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private LongSupplier mCallDurationSupplier;
+ @Mock
+ private LongSupplier mScanDurationSupplier;
+
+ private final MobileRadioPowerStatsCollector.Injector mInjector =
+ new MobileRadioPowerStatsCollector.Injector() {
+ @Override
+ public Handler getHandler() {
+ return mStatsRule.getHandler();
+ }
+
+ @Override
+ public Clock getClock() {
+ return mStatsRule.getMockClock();
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mPowerStatsUidResolver;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> VOLTAGE_MV;
+ }
+
+ @Override
+ public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
+ return mNetworkStatsSupplier;
+ }
+
+ @Override
+ public TelephonyManager getTelephonyManager() {
+ return mTelephonyManager;
+ }
+
+ @Override
+ public LongSupplier getCallDurationSupplier() {
+ return mCallDurationSupplier;
+ }
+
+ @Override
+ public LongSupplier getPhoneSignalScanDurationSupplier() {
+ return mScanDurationSupplier;
+ }
+ };
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+ when(mPowerStatsUidResolver.mapUid(anyInt()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ }
+
+ @Test
+ public void powerProfileModel() {
+ // No power monitoring hardware
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
+ .thenReturn(new int[0]);
+
+ mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator");
+
+ MobileRadioPowerStatsProcessor processor =
+ new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
+
+ AggregatedPowerStatsConfig.PowerComponent config =
+ new AggregatedPowerStatsConfig.PowerComponent(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+ .setProcessor(processor);
+
+ PowerComponentAggregatedPowerStats aggregatedStats =
+ new PowerComponentAggregatedPowerStats(
+ new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+
+ aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+ aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+ aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+ MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+ collector.setEnabled(true);
+
+ // Initial empty ModemActivityInfo.
+ mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
+
+ // Establish a baseline
+ aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+ // Turn the screen off after 2.5 seconds
+ aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+ 5000);
+
+ // Note application network activity
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
+
+ when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
+
+ ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+ new int[]{100, 200, 300, 400, 500}, 600);
+ mockModemActivityInfo(mai);
+
+ when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
+ when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
+
+ mStatsRule.setTime(10_000, 10_000);
+
+ PowerStats powerStats = collector.collectStats();
+
+ aggregatedStats.addPowerStats(powerStats, 10_000);
+
+ processor.finish(aggregatedStats);
+
+ MobileRadioPowerStatsLayout statsLayout =
+ new MobileRadioPowerStatsLayout(
+ aggregatedStats.getPowerStatsDescriptor());
+
+ // 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration)
+ // + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration)
+ // + 1440 mA * 300 ms (level 2 TX drain rate * level 2 TX duration)
+ // + 1800 mA * 400 ms (level 3 TX drain rate * level 3 TX duration)
+ // + 2160 mA * 500 ms (level 4 TX drain rate * level 4 TX duration)
+ // + 1440 mA * 600 ms (RX drain rate * RX duration)
+ // + 360 mA * 3000 ms (idle drain rate * idle duration)
+ // + 70 mA * 2000 ms (sleep drain rate * sleep duration)
+ // _________________
+ // = 4604000 mA-ms or 1.27888 mA-h
+ // 25% of 1.27888 = 0.319722
+ // 75% of 1.27888 = 0.959166
+ double totalPower = 0;
+ long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
+ aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.319722);
+ totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
+
+ aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.959166);
+ totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
+
+ assertThat(totalPower).isWithin(PRECISION).of(1.27888);
+
+ // 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration)
+ // + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration)
+ // + 1440 mA * 300 ms (level 2 TX drain rate * level 2 TX duration)
+ // + 1800 mA * 400 ms (level 3 TX drain rate * level 3 TX duration)
+ // + 2160 mA * 500 ms (level 4 TX drain rate * level 4 TX duration)
+ // + 1440 mA * 600 ms (RX drain rate * RX duration)
+ // _________________
+ // = 3384000 mA-ms or 0.94 mA-h
+ double uidPower1 = 0;
+ long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
+ aggregatedStats.getUidStats(uidStats, APP_UID,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.17625);
+ uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+ aggregatedStats.getUidStats(uidStats, APP_UID,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.17625);
+ uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+ aggregatedStats.getUidStats(uidStats, APP_UID,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.3525);
+ uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+ double uidPower2 = 0;
+ aggregatedStats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.05875);
+ uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
+
+ aggregatedStats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.17625);
+ uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
+
+ assertThat(uidPower1 + uidPower2)
+ .isWithin(PRECISION).of(0.94);
+
+ // 3/4 of total packets were sent by APP_UID so 75% of total
+ assertThat(uidPower1 / (uidPower1 + uidPower2))
+ .isWithin(PRECISION).of(0.75);
+ }
+
+ @Test
+ public void measuredEnergyModel() {
+ // PowerStats hardware is available
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
+ .thenReturn(new int[] {MOBILE_RADIO_ENERGY_CONSUMER_ID});
+
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
+ .initMeasuredEnergyStatsLocked();
+
+ MobileRadioPowerStatsProcessor processor =
+ new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
+
+ AggregatedPowerStatsConfig.PowerComponent config =
+ new AggregatedPowerStatsConfig.PowerComponent(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+ .setProcessor(processor);
+
+ PowerComponentAggregatedPowerStats aggregatedStats =
+ new PowerComponentAggregatedPowerStats(
+ new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+
+ aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+ aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+ aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+ MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+ collector.setEnabled(true);
+
+ // Initial empty ModemActivityInfo.
+ mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
+
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(
+ new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID}))
+ .thenReturn(new long[]{0});
+
+ // Establish a baseline
+ aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+ // Turn the screen off after 2.5 seconds
+ aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+ 5000);
+
+ // Note application network activity
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
+
+ when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
+
+ ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+ new int[]{100, 200, 300, 400, 500}, 600);
+ mockModemActivityInfo(mai);
+
+ mStatsRule.setTime(10_000, 10_000);
+
+ long energyUws = 10_000_000L * VOLTAGE_MV / 1000L;
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(
+ new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws});
+
+ when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
+ when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
+
+ PowerStats powerStats = collector.collectStats();
+
+ aggregatedStats.addPowerStats(powerStats, 10_000);
+
+ processor.finish(aggregatedStats);
+
+ MobileRadioPowerStatsLayout statsLayout =
+ new MobileRadioPowerStatsLayout(
+ aggregatedStats.getPowerStatsDescriptor());
+
+ // 10_000_000 micro-Coulomb * 1/1000 milli/micro * 1/3600 hour/second = 2.77778 mAh
+ double totalPower = 0;
+ long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
+ aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.671837);
+ totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
+ assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.022494);
+ totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats);
+
+ aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(2.01596);
+ totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
+ assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.067484);
+ totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats);
+
+ // These estimates are supposed to add up to the measured energy, 2.77778 mAh
+ assertThat(totalPower).isWithin(PRECISION).of(2.77778);
+
+ double uidPower1 = 0;
+ long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
+ aggregatedStats.getUidStats(uidStats, APP_UID,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.198236);
+ uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+ aggregatedStats.getUidStats(uidStats, APP_UID,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.198236);
+ uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+ aggregatedStats.getUidStats(uidStats, APP_UID,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.396473);
+ uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+ double uidPower2 = 0;
+ aggregatedStats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.066078);
+ uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
+
+ aggregatedStats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.198236);
+ uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
+
+ // Total power attributed to apps is significantly less than the grand total,
+ // because we only attribute TX/RX to apps but not maintaining a connection with the cell.
+ assertThat(uidPower1 + uidPower2)
+ .isWithin(PRECISION).of(1.057259);
+
+ // 3/4 of total packets were sent by APP_UID so 75% of total RX/TX power is attributed to it
+ assertThat(uidPower1 / (uidPower1 + uidPower2))
+ .isWithin(PRECISION).of(0.75);
+ }
+
+ private int[] states(int... states) {
+ return states;
+ }
+
+ private void mockModemActivityInfo(ModemActivityInfo emptyMai) {
+ doAnswer(invocation -> {
+ OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
+ receiver = invocation.getArgument(1);
+ receiver.onResult(emptyMai);
+ return null;
+ }).when(mTelephonyManager).requestModemActivityInfo(any(), any());
+ }
+
+ private NetworkStats mockNetworkStats(int elapsedTime, int initialSize,
+ NetworkStats.Entry... entries) {
+ NetworkStats stats;
+ if (RavenwoodRule.isOnRavenwood()) {
+ stats = mock(NetworkStats.class);
+ when(stats.iterator()).thenAnswer(inv -> List.of(entries).iterator());
+ } else {
+ stats = new NetworkStats(elapsedTime, initialSize);
+ for (NetworkStats.Entry entry : entries) {
+ stats = stats.addEntry(entry);
+ }
+ }
+ return stats;
+ }
+
+ private static NetworkStats.Entry mockNetworkStatsEntry(@Nullable String iface, int uid,
+ int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes,
+ long rxPackets, long txBytes, long txPackets, long operations) {
+ if (RavenwoodRule.isOnRavenwood()) {
+ NetworkStats.Entry entry = mock(NetworkStats.Entry.class);
+ when(entry.getUid()).thenReturn(uid);
+ when(entry.getMetered()).thenReturn(metered);
+ when(entry.getRoaming()).thenReturn(roaming);
+ when(entry.getDefaultNetwork()).thenReturn(defaultNetwork);
+ when(entry.getRxBytes()).thenReturn(rxBytes);
+ when(entry.getRxPackets()).thenReturn(rxPackets);
+ when(entry.getTxBytes()).thenReturn(txBytes);
+ when(entry.getTxPackets()).thenReturn(txPackets);
+ when(entry.getOperations()).thenReturn(operations);
+ return entry;
+ } else {
+ return new NetworkStats.Entry(iface, uid, set, tag, metered,
+ roaming, defaultNetwork, rxBytes, rxPackets, txBytes, txPackets, operations);
+ }
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 9f06913..1d48975 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -35,6 +35,7 @@
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
import com.android.internal.os.KernelSingleUidTimeReader;
+import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import com.android.internal.power.EnergyConsumerStats;
@@ -52,6 +53,8 @@
// The mNetworkStats will be used for both wifi and mobile categories
private NetworkStats mNetworkStats;
private DummyExternalStatsSync mExternalStatsSync = new DummyExternalStatsSync();
+ public static final BatteryStatsConfig DEFAULT_CONFIG =
+ new BatteryStatsConfig.Builder().build();
MockBatteryStatsImpl() {
this(new MockClock());
@@ -66,15 +69,18 @@
}
MockBatteryStatsImpl(Clock clock, File historyDirectory, Handler handler) {
- this(clock, historyDirectory, handler, new PowerStatsUidResolver());
+ this(DEFAULT_CONFIG, clock, historyDirectory, handler, new PowerStatsUidResolver());
}
- MockBatteryStatsImpl(Clock clock, File historyDirectory, Handler handler,
- PowerStatsUidResolver powerStatsUidResolver) {
- super(clock, historyDirectory, handler, powerStatsUidResolver,
- mock(FrameworkStatsLogger.class), mock(BatteryStatsHistory.TraceDelegate.class),
+ MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, File historyDirectory,
+ Handler handler, PowerStatsUidResolver powerStatsUidResolver) {
+ super(config, clock, new MonotonicClock(0, clock), historyDirectory, handler,
+ mock(PlatformIdleStateCallback.class), mock(EnergyStatsRetriever.class),
+ mock(UserInfoProvider.class), mock(PowerProfile.class),
+ new CpuScalingPolicies(new SparseArray<>(), new SparseArray<>()),
+ powerStatsUidResolver, mock(FrameworkStatsLogger.class),
+ mock(BatteryStatsHistory.TraceDelegate.class),
mock(BatteryStatsHistory.EventLogger.class));
- initTimersAndCounters();
setMaxHistoryBuffer(128 * 1024);
setExternalStatsSyncLocked(mExternalStatsSync);
@@ -276,10 +282,6 @@
public void writeSyncLocked() {
}
- public void setHandler(Handler handler) {
- mHandler = handler;
- }
-
@Override
protected void updateBatteryPropertiesLocked() {
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
new file mode 100644
index 0000000..dadcf3f
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.Handler;
+import android.os.OutcomeReceiver;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.telephony.ModemActivityInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.os.Clock;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+public class PhoneCallPowerStatsProcessorTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ private static final double PRECISION = 0.00001;
+ private static final int VOLTAGE_MV = 3500;
+
+ @Rule(order = 1)
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+ @Mock
+ private Context mContext;
+ @Mock
+ private PowerStatsUidResolver mPowerStatsUidResolver;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ @Mock
+ private Supplier<NetworkStats> mNetworkStatsSupplier;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private LongSupplier mCallDurationSupplier;
+ @Mock
+ private LongSupplier mScanDurationSupplier;
+
+ private final MobileRadioPowerStatsCollector.Injector mInjector =
+ new MobileRadioPowerStatsCollector.Injector() {
+ @Override
+ public Handler getHandler() {
+ return mStatsRule.getHandler();
+ }
+
+ @Override
+ public Clock getClock() {
+ return mStatsRule.getMockClock();
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mPowerStatsUidResolver;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> VOLTAGE_MV;
+ }
+
+ @Override
+ public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
+ return mNetworkStatsSupplier;
+ }
+
+ @Override
+ public TelephonyManager getTelephonyManager() {
+ return mTelephonyManager;
+ }
+
+ @Override
+ public LongSupplier getCallDurationSupplier() {
+ return mCallDurationSupplier;
+ }
+
+ @Override
+ public LongSupplier getPhoneSignalScanDurationSupplier() {
+ return mScanDurationSupplier;
+ }
+ };
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+ when(mPowerStatsUidResolver.mapUid(anyInt()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+
+ // No power monitoring hardware
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
+ .thenReturn(new int[0]);
+
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem");
+ }
+
+ @Test
+ public void copyEstimatesFromMobileRadioPowerStats() {
+ MobileRadioPowerStatsProcessor mobileStatsProcessor =
+ new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
+
+ PhoneCallPowerStatsProcessor phoneStatsProcessor =
+ new PhoneCallPowerStatsProcessor();
+
+ AggregatedPowerStatsConfig aggregatedPowerStatsConfig = new AggregatedPowerStatsConfig();
+ aggregatedPowerStatsConfig.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+ .setProcessor(mobileStatsProcessor);
+ aggregatedPowerStatsConfig.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE,
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .setProcessor(phoneStatsProcessor);
+
+ AggregatedPowerStats aggregatedPowerStats =
+ new AggregatedPowerStats(aggregatedPowerStatsConfig);
+ PowerComponentAggregatedPowerStats mobileRadioStats =
+ aggregatedPowerStats.getPowerComponentStats(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+
+ aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0);
+ aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+
+ MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+ collector.setEnabled(true);
+
+ // Initial empty ModemActivityInfo.
+ mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
+
+ // Establish a baseline
+ aggregatedPowerStats.addPowerStats(collector.collectStats(), 0);
+
+ // Turn the screen off after 2.5 seconds
+ aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+
+ ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+ new int[]{100, 200, 300, 400, 500}, 600);
+ mockModemActivityInfo(mai);
+
+ // A phone call was made
+ when(mCallDurationSupplier.getAsLong()).thenReturn(7000L);
+
+ mStatsRule.setTime(10_000, 10_000);
+
+ aggregatedPowerStats.addPowerStats(collector.collectStats(), 10_000);
+
+ mobileStatsProcessor.finish(mobileRadioStats);
+
+ PowerComponentAggregatedPowerStats stats =
+ aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_PHONE);
+ phoneStatsProcessor.finish(stats);
+
+ PowerStatsLayout statsLayout =
+ new PowerStatsLayout(stats.getPowerStatsDescriptor());
+
+ long[] deviceStats = new long[stats.getPowerStatsDescriptor().statsArrayLength];
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.7);
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(2.1);
+ }
+
+ private void mockModemActivityInfo(ModemActivityInfo emptyMai) {
+ doAnswer(invocation -> {
+ OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
+ receiver = invocation.getArgument(1);
+ receiver.onResult(emptyMai);
+ return null;
+ }).when(mTelephonyManager).requestModemActivityInfo(any(), any());
+ }
+
+ private int[] states(int... states) {
+ return states;
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
index 2ea86a4..3929137 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
@@ -58,7 +58,7 @@
@Before
public void setup() throws ParseException {
- mHistory = new BatteryStatsHistory(1024,
+ mHistory = new BatteryStatsHistory(null, null, 0, 1024,
mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
mMonotonicClock, mock(BatteryStatsHistory.TraceDelegate.class), null);
@@ -76,9 +76,8 @@
@Test
public void stateUpdates() {
- PowerStats.Descriptor descriptor =
- new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
- new PersistableBundle());
+ PowerStats.Descriptor descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT,
+ "majorDrain", 1, null, 0, 1, new PersistableBundle());
PowerStats powerStats = new PowerStats(descriptor);
mClock.currentTime = 1222156800000L; // An important date in world history
@@ -186,9 +185,8 @@
@Test
public void incompatiblePowerStats() {
- PowerStats.Descriptor descriptor =
- new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
- new PersistableBundle());
+ PowerStats.Descriptor descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT,
+ "majorDrain", 1, null, 0, 1, new PersistableBundle());
PowerStats powerStats = new PowerStats(descriptor);
mHistory.forceRecordAllHistory();
@@ -209,7 +207,7 @@
advance(1000);
- descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
+ descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, null, 0, 1,
PersistableBundle.forPair("something", "changed"));
powerStats = new PowerStats(descriptor);
powerStats.stats = new long[]{20000};
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
index 17a7d3e..89d6c1c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
@@ -18,11 +18,22 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.PersistableBundle;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.power.PowerStatsInternal;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -34,6 +45,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PowerStatsCollectorTest {
@@ -52,12 +66,12 @@
public void setup() {
mHandlerThread.start();
mHandler = mHandlerThread.getThreadHandler();
- mCollector = new PowerStatsCollector(mHandler,
- 60000,
+ mCollector = new PowerStatsCollector(mHandler, 60000, mock(PowerStatsUidResolver.class),
mMockClock) {
@Override
protected PowerStats collectStats() {
- return new PowerStats(new PowerStats.Descriptor(0, 0, 0, new PersistableBundle()));
+ return new PowerStats(
+ new PowerStats.Descriptor(0, 0, null, 0, 0, new PersistableBundle()));
}
};
mCollector.addConsumer(stats -> mCollectedStats = stats);
@@ -92,4 +106,74 @@
mHandler.post(done::open);
done.block();
}
+
+ @Test
+ @DisabledOnRavenwood
+ public void consumedEnergyRetriever() throws Exception {
+ PowerStatsInternal powerStatsInternal = mock(PowerStatsInternal.class);
+ mockEnergyConsumers(powerStatsInternal);
+
+ PowerStatsCollector.ConsumedEnergyRetrieverImpl retriever =
+ new PowerStatsCollector.ConsumedEnergyRetrieverImpl(powerStatsInternal);
+ int[] energyConsumerIds = retriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER);
+ assertThat(energyConsumerIds).isEqualTo(new int[]{1, 2});
+ long[] energy = retriever.getConsumedEnergyUws(energyConsumerIds);
+ assertThat(energy).isEqualTo(new long[]{1000, 2000});
+ energy = retriever.getConsumedEnergyUws(energyConsumerIds);
+ assertThat(energy).isEqualTo(new long[]{1500, 2700});
+ }
+
+ @SuppressWarnings("unchecked")
+ private void mockEnergyConsumers(PowerStatsInternal powerStatsInternal) throws Exception {
+ when(powerStatsInternal.getEnergyConsumerInfo())
+ .thenReturn(new EnergyConsumer[]{
+ new EnergyConsumer() {{
+ id = 1;
+ type = EnergyConsumerType.CPU_CLUSTER;
+ ordinal = 0;
+ name = "CPU0";
+ }},
+ new EnergyConsumer() {{
+ id = 2;
+ type = EnergyConsumerType.CPU_CLUSTER;
+ ordinal = 1;
+ name = "CPU4";
+ }},
+ new EnergyConsumer() {{
+ id = 3;
+ type = EnergyConsumerType.BLUETOOTH;
+ name = "BT";
+ }},
+ });
+
+ CompletableFuture<EnergyConsumerResult[]> future1 = mock(CompletableFuture.class);
+ when(future1.get(anyLong(), any(TimeUnit.class)))
+ .thenReturn(new EnergyConsumerResult[]{
+ new EnergyConsumerResult() {{
+ id = 1;
+ energyUWs = 1000;
+ }},
+ new EnergyConsumerResult() {{
+ id = 2;
+ energyUWs = 2000;
+ }}
+ });
+
+ CompletableFuture<EnergyConsumerResult[]> future2 = mock(CompletableFuture.class);
+ when(future2.get(anyLong(), any(TimeUnit.class)))
+ .thenReturn(new EnergyConsumerResult[]{
+ new EnergyConsumerResult() {{
+ id = 1;
+ energyUWs = 1500;
+ }},
+ new EnergyConsumerResult() {{
+ id = 2;
+ energyUWs = 2700;
+ }}
+ });
+
+ when(powerStatsInternal.getEnergyConsumedAsync(eq(new int[]{1, 2})))
+ .thenReturn(future1)
+ .thenReturn(future2);
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
index 18d7b90..412fc88 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
@@ -77,7 +77,7 @@
private PowerStatsStore mPowerStatsStore;
private PowerStatsAggregator mPowerStatsAggregator;
private BatteryStatsHistory mHistory;
- private CpuPowerStatsCollector.CpuStatsArrayLayout mCpuStatsArrayLayout;
+ private CpuPowerStatsLayout mCpuStatsArrayLayout;
private PowerStats.Descriptor mPowerStatsDescriptor;
@Before
@@ -93,7 +93,7 @@
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
.setProcessor(
- new CpuAggregatedPowerStatsProcessor(mStatsRule.getPowerProfile(),
+ new CpuPowerStatsProcessor(mStatsRule.getPowerProfile(),
mStatsRule.getCpuScalingPolicies()));
mPowerStatsStore = new PowerStatsStore(storeDirectory, new TestHandler(), config);
@@ -102,9 +102,10 @@
mMonotonicClock, null, null);
mPowerStatsAggregator = new PowerStatsAggregator(config, mHistory);
- mCpuStatsArrayLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ mCpuStatsArrayLayout = new CpuPowerStatsLayout();
mCpuStatsArrayLayout.addDeviceSectionCpuTimeByScalingStep(1);
mCpuStatsArrayLayout.addDeviceSectionCpuTimeByCluster(1);
+ mCpuStatsArrayLayout.addDeviceSectionUsageDuration();
mCpuStatsArrayLayout.addDeviceSectionPowerEstimate();
mCpuStatsArrayLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0});
mCpuStatsArrayLayout.addUidSectionPowerEstimate();
@@ -113,7 +114,7 @@
mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
mCpuStatsArrayLayout.getDeviceStatsArrayLength(),
- mCpuStatsArrayLayout.getUidStatsArrayLength(), extras);
+ null, 0, mCpuStatsArrayLayout.getUidStatsArrayLength(), extras);
}
@Test
@@ -126,20 +127,20 @@
BatteryUsageStats actual = builder.build();
String message = "Actual BatteryUsageStats: " + actual;
- assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
- assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
+ assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+ assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_ANY, 13.5);
+ BatteryConsumer.PROCESS_STATE_ANY, 3.97099);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_FOREGROUND, 7.47);
+ BatteryConsumer.PROCESS_STATE_FOREGROUND, 2.198082);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_BACKGROUND, 6.03);
+ BatteryConsumer.PROCESS_STATE_BACKGROUND, 1.772916);
assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_ANY, 12.03);
+ BatteryConsumer.PROCESS_STATE_ANY, 3.538999);
assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 12.03);
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 3.538999);
actual.close();
}
@@ -154,20 +155,20 @@
BatteryUsageStats actual = builder.build();
String message = "Actual BatteryUsageStats: " + actual;
- assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 15.4);
- assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 15.4);
+ assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 4.526749);
+ assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 4.526749);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_ANY, 4.06);
+ BatteryConsumer.PROCESS_STATE_ANY, 1.193332);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_FOREGROUND, 1.35);
+ BatteryConsumer.PROCESS_STATE_FOREGROUND, 0.397749);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_BACKGROUND, 2.70);
+ BatteryConsumer.PROCESS_STATE_BACKGROUND, 0.795583);
assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_ANY, 11.33);
+ BatteryConsumer.PROCESS_STATE_ANY, 3.333249);
assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 11.33);
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 3.333249);
actual.close();
}
@@ -182,13 +183,13 @@
BatteryUsageStats actual = builder.build();
String message = "Actual BatteryUsageStats: " + actual;
- assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
- assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
+ assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+ assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_ANY, 13.5);
+ BatteryConsumer.PROCESS_STATE_ANY, 3.97099);
assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_ANY, 12.03);
+ BatteryConsumer.PROCESS_STATE_ANY, 3.538999);
UidBatteryConsumer uidScope = actual.getUidBatteryConsumers().stream()
.filter(us -> us.getUid() == APP_UID1).findFirst().orElse(null);
// There shouldn't be any per-procstate data
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java
similarity index 90%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java
index af83be0..02e446a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java
@@ -35,7 +35,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class AggregatedPowerStatsProcessorTest {
+public class PowerStatsProcessorTest {
@Test
public void createPowerEstimationPlan_allDeviceStatesPresentInUidStates() {
@@ -44,8 +44,8 @@
.trackDeviceStates(STATE_POWER, STATE_SCREEN)
.trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE);
- AggregatedPowerStatsProcessor.PowerEstimationPlan plan =
- new AggregatedPowerStatsProcessor.PowerEstimationPlan(config);
+ PowerStatsProcessor.PowerEstimationPlan plan =
+ new PowerStatsProcessor.PowerEstimationPlan(config);
assertThat(deviceStateEstimatesToStrings(plan))
.containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]");
assertThat(combinedDeviceStatsToStrings(plan))
@@ -65,8 +65,8 @@
.trackDeviceStates(STATE_POWER, STATE_SCREEN)
.trackUidStates(STATE_POWER, STATE_PROCESS_STATE);
- AggregatedPowerStatsProcessor.PowerEstimationPlan plan =
- new AggregatedPowerStatsProcessor.PowerEstimationPlan(config);
+ PowerStatsProcessor.PowerEstimationPlan plan =
+ new PowerStatsProcessor.PowerEstimationPlan(config);
assertThat(deviceStateEstimatesToStrings(plan))
.containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]");
@@ -81,13 +81,13 @@
}
private static List<String> deviceStateEstimatesToStrings(
- AggregatedPowerStatsProcessor.PowerEstimationPlan plan) {
+ PowerStatsProcessor.PowerEstimationPlan plan) {
return plan.deviceStateEstimations.stream()
.map(dse -> dse.stateValues).map(Arrays::toString).toList();
}
private static List<String> combinedDeviceStatsToStrings(
- AggregatedPowerStatsProcessor.PowerEstimationPlan plan) {
+ PowerStatsProcessor.PowerEstimationPlan plan) {
return plan.combinedDeviceStateEstimations.stream()
.map(cds -> cds.deviceStateEstimations)
.map(dses -> dses.stream()
@@ -97,7 +97,7 @@
}
private static List<String> uidStateEstimatesToStrings(
- AggregatedPowerStatsProcessor.PowerEstimationPlan plan,
+ PowerStatsProcessor.PowerEstimationPlan plan,
AggregatedPowerStatsConfig.PowerComponent config) {
MultiStateStats.States[] uidStateConfig = config.getUidStateConfig();
return plan.uidStateEstimates.stream()
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java
index 80cbe0d..d67d408 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java
@@ -18,11 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.KernelSingleProcessCpuThreadReader;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -30,7 +33,10 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Kernel dependency")
public class SystemServerCpuThreadReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
@Test
public void testReadDelta() throws IOException {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
index 8e53d52..ef0b570 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
@@ -31,9 +31,10 @@
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.KernelCpuSpeedReader;
@@ -46,7 +47,6 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -55,17 +55,21 @@
import java.util.Collection;
@SmallTest
-@RunWith(AndroidJUnit4.class)
@SuppressWarnings("GuardedBy")
public class SystemServicePowerCalculatorTest {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Rule(order = 1)
+ public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood()
+ ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
+ : DeviceFlagsValueProvider.createCheckFlagsRule();
private static final double PRECISION = 0.000001;
private static final int APP_UID1 = 100;
private static final int APP_UID2 = 200;
- @Rule
+ @Rule(order = 2)
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
.setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200})
diff --git a/services/tests/selinux/Android.bp b/services/tests/selinux/Android.bp
index f387238..12a7038 100644
--- a/services/tests/selinux/Android.bp
+++ b/services/tests/selinux/Android.bp
@@ -52,6 +52,7 @@
"androidx.test.ext.junit",
"androidx.test.ext.truth",
"androidx.test.runner",
+ "compatibility-device-util-axt",
"services.core",
],
test_suites: [
diff --git a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsBuilderTest.java b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsBuilderTest.java
index b36c9bd..e86108d 100644
--- a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsBuilderTest.java
+++ b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsBuilderTest.java
@@ -15,98 +15,144 @@
*/
package com.android.server.selinux;
-import static com.android.server.selinux.SelinuxAuditLogBuilder.PATH_MATCHER;
-import static com.android.server.selinux.SelinuxAuditLogBuilder.SCONTEXT_MATCHER;
-import static com.android.server.selinux.SelinuxAuditLogBuilder.TCONTEXT_MATCHER;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import static com.android.server.selinux.SelinuxAuditLogBuilder.toCategories;
import static com.google.common.truth.Truth.assertThat;
+import android.provider.DeviceConfig;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.server.selinux.SelinuxAuditLogBuilder.SelinuxAuditLog;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.regex.Matcher;
+
@RunWith(AndroidJUnit4.class)
public class SelinuxAuditLogsBuilderTest {
- private final SelinuxAuditLogBuilder mAuditLogBuilder = new SelinuxAuditLogBuilder();
+ private static final String TEST_DOMAIN = "test_domain";
+
+ private SelinuxAuditLogBuilder mAuditLogBuilder;
+ private Matcher mScontextMatcher;
+ private Matcher mTcontextMatcher;
+ private Matcher mPathMatcher;
+
+ @Before
+ public void setUp() {
+ runWithShellPermissionIdentity(
+ () ->
+ DeviceConfig.setLocalOverride(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ SelinuxAuditLogBuilder.CONFIG_SELINUX_AUDIT_DOMAIN,
+ TEST_DOMAIN));
+
+ mAuditLogBuilder = new SelinuxAuditLogBuilder();
+ mScontextMatcher = mAuditLogBuilder.mScontextMatcher;
+ mTcontextMatcher = mAuditLogBuilder.mTcontextMatcher;
+ mPathMatcher = mAuditLogBuilder.mPathMatcher;
+ }
+
+ @After
+ public void tearDown() {
+ runWithShellPermissionIdentity(() -> DeviceConfig.clearAllLocalOverrides());
+ }
@Test
public void testMatcher_scontext() {
- assertThat(SCONTEXT_MATCHER.reset("u:r:sdk_sandbox_audit:s0").matches()).isTrue();
- assertThat(SCONTEXT_MATCHER.group("stype")).isEqualTo("sdk_sandbox_audit");
- assertThat(SCONTEXT_MATCHER.group("scategories")).isNull();
+ assertThat(mScontextMatcher.reset("u:r:" + TEST_DOMAIN + ":s0").matches()).isTrue();
+ assertThat(mScontextMatcher.group("stype")).isEqualTo(TEST_DOMAIN);
+ assertThat(mScontextMatcher.group("scategories")).isNull();
- assertThat(SCONTEXT_MATCHER.reset("u:r:sdk_sandbox_audit:s0:c123,c456").matches()).isTrue();
- assertThat(SCONTEXT_MATCHER.group("stype")).isEqualTo("sdk_sandbox_audit");
- assertThat(toCategories(SCONTEXT_MATCHER.group("scategories")))
+ assertThat(mScontextMatcher.reset("u:r:" + TEST_DOMAIN + ":s0:c123,c456").matches())
+ .isTrue();
+ assertThat(mScontextMatcher.group("stype")).isEqualTo(TEST_DOMAIN);
+ assertThat(toCategories(mScontextMatcher.group("scategories")))
.isEqualTo(new int[] {123, 456});
- assertThat(SCONTEXT_MATCHER.reset("u:r:not_sdk_sandbox:s0").matches()).isFalse();
- assertThat(SCONTEXT_MATCHER.reset("u:object_r:sdk_sandbox_audit:s0").matches()).isFalse();
- assertThat(SCONTEXT_MATCHER.reset("u:r:sdk_sandbox_audit:s0:p123").matches()).isFalse();
+ assertThat(mScontextMatcher.reset("u:r:wrong_domain:s0").matches()).isFalse();
+ assertThat(mScontextMatcher.reset("u:object_r:" + TEST_DOMAIN + ":s0").matches()).isFalse();
+ assertThat(mScontextMatcher.reset("u:r:" + TEST_DOMAIN + ":s0:p123").matches()).isFalse();
}
@Test
public void testMatcher_tcontext() {
- assertThat(TCONTEXT_MATCHER.reset("u:object_r:target_type:s0").matches()).isTrue();
- assertThat(TCONTEXT_MATCHER.group("ttype")).isEqualTo("target_type");
- assertThat(TCONTEXT_MATCHER.group("tcategories")).isNull();
+ assertThat(mTcontextMatcher.reset("u:object_r:target_type:s0").matches()).isTrue();
+ assertThat(mTcontextMatcher.group("ttype")).isEqualTo("target_type");
+ assertThat(mTcontextMatcher.group("tcategories")).isNull();
- assertThat(TCONTEXT_MATCHER.reset("u:object_r:target_type2:s0:c666").matches()).isTrue();
- assertThat(TCONTEXT_MATCHER.group("ttype")).isEqualTo("target_type2");
- assertThat(toCategories(TCONTEXT_MATCHER.group("tcategories"))).isEqualTo(new int[] {666});
+ assertThat(mTcontextMatcher.reset("u:object_r:target_type2:s0:c666").matches()).isTrue();
+ assertThat(mTcontextMatcher.group("ttype")).isEqualTo("target_type2");
+ assertThat(toCategories(mTcontextMatcher.group("tcategories"))).isEqualTo(new int[] {666});
- assertThat(TCONTEXT_MATCHER.reset("u:r:target_type:s0").matches()).isFalse();
- assertThat(TCONTEXT_MATCHER.reset("u:r:sdk_sandbox_audit:s0:x456").matches()).isFalse();
+ assertThat(mTcontextMatcher.reset("u:r:target_type:s0").matches()).isFalse();
+ assertThat(mTcontextMatcher.reset("u:r:" + TEST_DOMAIN + ":s0:x456").matches()).isFalse();
}
@Test
public void testMatcher_path() {
- assertThat(PATH_MATCHER.reset("\"/data\"").matches()).isTrue();
- assertThat(PATH_MATCHER.group("path")).isEqualTo("/data");
- assertThat(PATH_MATCHER.reset("\"/data/local\"").matches()).isTrue();
- assertThat(PATH_MATCHER.group("path")).isEqualTo("/data/local");
- assertThat(PATH_MATCHER.reset("\"/data/local/tmp\"").matches()).isTrue();
- assertThat(PATH_MATCHER.group("path")).isEqualTo("/data/local");
+ assertThat(mPathMatcher.reset("\"/data\"").matches()).isTrue();
+ assertThat(mPathMatcher.group("path")).isEqualTo("/data");
+ assertThat(mPathMatcher.reset("\"/data/local\"").matches()).isTrue();
+ assertThat(mPathMatcher.group("path")).isEqualTo("/data/local");
+ assertThat(mPathMatcher.reset("\"/data/local/tmp\"").matches()).isTrue();
+ assertThat(mPathMatcher.group("path")).isEqualTo("/data/local");
- assertThat(PATH_MATCHER.reset("\"/data/local").matches()).isFalse();
- assertThat(PATH_MATCHER.reset("\"_data_local\"").matches()).isFalse();
+ assertThat(mPathMatcher.reset("\"/data/local").matches()).isFalse();
+ assertThat(mPathMatcher.reset("\"_data_local\"").matches()).isFalse();
+ }
+
+ @Test
+ public void testMatcher_scontextDefaultConfig() {
+ runWithShellPermissionIdentity(
+ () ->
+ DeviceConfig.clearLocalOverride(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ SelinuxAuditLogBuilder.CONFIG_SELINUX_AUDIT_DOMAIN));
+
+ Matcher scontexMatcher = new SelinuxAuditLogBuilder().mScontextMatcher;
+
+ assertThat(scontexMatcher.reset("u:r:" + TEST_DOMAIN + ":s0").matches()).isFalse();
+ assertThat(scontexMatcher.reset("u:r:" + TEST_DOMAIN + ":s0:c123,c456").matches())
+ .isFalse();
+ assertThat(scontexMatcher.reset("u:r:wrong_domain:s0").matches()).isFalse();
}
@Test
public void testSelinuxAuditLogsBuilder_noOptionals() {
mAuditLogBuilder.reset(
- "granted { p } scontext=u:r:sdk_sandbox_audit:s0 tcontext=u:object_r:t:s0"
+ "granted { p } scontext=u:r:"
+ + TEST_DOMAIN
+ + ":s0 tcontext=u:object_r:t:s0"
+ " tclass=c");
- assertAuditLog(
- mAuditLogBuilder.build(), true, new String[] {"p"}, "sdk_sandbox_audit", "t", "c");
+ assertAuditLog(mAuditLogBuilder.build(), true, new String[] {"p"}, TEST_DOMAIN, "t", "c");
mAuditLogBuilder.reset(
"tclass=c2 granted { p2 } tcontext=u:object_r:t2:s0"
- + " scontext=u:r:sdk_sandbox_audit:s0");
+ + " scontext=u:r:"
+ + TEST_DOMAIN
+ + ":s0");
assertAuditLog(
- mAuditLogBuilder.build(),
- true,
- new String[] {"p2"},
- "sdk_sandbox_audit",
- "t2",
- "c2");
+ mAuditLogBuilder.build(), true, new String[] {"p2"}, TEST_DOMAIN, "t2", "c2");
}
@Test
public void testSelinuxAuditLogsBuilder_withCategories() {
mAuditLogBuilder.reset(
- "granted { p } scontext=u:r:sdk_sandbox_audit:s0:c123"
+ "granted { p } scontext=u:r:"
+ + TEST_DOMAIN
+ + ":s0:c123"
+ " tcontext=u:object_r:t:s0:c456,c666 tclass=c");
assertAuditLog(
mAuditLogBuilder.build(),
true,
new String[] {"p"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
new int[] {123},
"t",
new int[] {456, 666},
@@ -118,13 +164,15 @@
@Test
public void testSelinuxAuditLogsBuilder_withPath() {
mAuditLogBuilder.reset(
- "granted { p } scontext=u:r:sdk_sandbox_audit:s0 path=\"/very/long/path\""
+ "granted { p } scontext=u:r:"
+ + TEST_DOMAIN
+ + ":s0 path=\"/very/long/path\""
+ " tcontext=u:object_r:t:s0 tclass=c");
assertAuditLog(
mAuditLogBuilder.build(),
true,
new String[] {"p"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"t",
null,
@@ -136,13 +184,15 @@
@Test
public void testSelinuxAuditLogsBuilder_withPermissive() {
mAuditLogBuilder.reset(
- "granted { p } scontext=u:r:sdk_sandbox_audit:s0 permissive=0"
+ "granted { p } scontext=u:r:"
+ + TEST_DOMAIN
+ + ":s0 permissive=0"
+ " tcontext=u:object_r:t:s0 tclass=c");
assertAuditLog(
mAuditLogBuilder.build(),
true,
new String[] {"p"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"t",
null,
@@ -151,13 +201,15 @@
false);
mAuditLogBuilder.reset(
- "granted { p } scontext=u:r:sdk_sandbox_audit:s0 tcontext=u:object_r:t:s0 tclass=c"
+ "granted { p } scontext=u:r:"
+ + TEST_DOMAIN
+ + ":s0 tcontext=u:object_r:t:s0 tclass=c"
+ " permissive=1");
assertAuditLog(
mAuditLogBuilder.build(),
true,
new String[] {"p"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"t",
null,
@@ -166,6 +218,40 @@
true);
}
+ @Test
+ public void testSelinuxAuditLogsBuilder_wrongConfig() {
+ String notARegexDomain = "not]a[regex";
+ runWithShellPermissionIdentity(
+ () ->
+ DeviceConfig.setLocalOverride(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ SelinuxAuditLogBuilder.CONFIG_SELINUX_AUDIT_DOMAIN,
+ notARegexDomain));
+ SelinuxAuditLogBuilder noOpBuilder = new SelinuxAuditLogBuilder();
+
+ noOpBuilder.reset(
+ "granted { p } scontext=u:r:"
+ + TEST_DOMAIN
+ + ":s0 tcontext=u:object_r:t:s0 tclass=c");
+ assertThat(noOpBuilder.build()).isNull();
+ noOpBuilder.reset(
+ "granted { p } scontext=u:r:"
+ + TEST_DOMAIN
+ + ":s0:c123 tcontext=u:object_r:t:s0:c456,c666 tclass=c");
+ assertThat(noOpBuilder.build()).isNull();
+ noOpBuilder.reset(
+ "granted { p } scontext=u:r:"
+ + TEST_DOMAIN
+ + ":s0 path=\"/very/long/path\""
+ + " tcontext=u:object_r:t:s0 tclass=c");
+ assertThat(noOpBuilder.build()).isNull();
+ noOpBuilder.reset(
+ "granted { p } scontext=u:r:"
+ + TEST_DOMAIN
+ + ":s0 permissive=0 tcontext=u:object_r:t:s0 tclass=c");
+ assertThat(noOpBuilder.build()).isNull();
+ }
+
private void assertAuditLog(
SelinuxAuditLog auditLog,
boolean granted,
diff --git a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java
index 4a70ad3..b6ccf5e 100644
--- a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java
+++ b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.selinux;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -27,6 +28,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
+import android.provider.DeviceConfig;
import android.util.EventLog;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -50,6 +52,7 @@
// Fake tag to use for testing
private static final int ANSWER_TAG = 42;
+ private static final String TEST_DOMAIN = "test_domain";
private final MockClock mClock = new MockClock();
@@ -64,6 +67,14 @@
@Before
public void setUp() {
+ runWithShellPermissionIdentity(
+ () ->
+ DeviceConfig.setLocalOverride(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ SelinuxAuditLogBuilder.CONFIG_SELINUX_AUDIT_DOMAIN,
+ TEST_DOMAIN));
+
+ mSelinuxAutidLogsCollector.setStopRequested(false);
// move the clock forward for the limiters.
mClock.currentTimeMillis += Duration.ofHours(1).toMillis();
// Ignore what was written in the event logs by previous tests.
@@ -74,13 +85,14 @@
@After
public void tearDown() {
+ runWithShellPermissionIdentity(() -> DeviceConfig.clearAllLocalOverrides());
mMockitoSession.finishMocking();
}
@Test
- public void testWriteSdkSandboxAuditLogs() {
- writeTestLog("granted", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm1", "sdk_sandbox_audit", "ttype1", "tclass1");
+ public void testWriteAuditLogs() {
+ writeTestLog("granted", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm1", TEST_DOMAIN, "ttype1", "tclass1");
boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
@@ -91,7 +103,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
true,
new String[] {"perm"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"ttype",
null,
@@ -104,7 +116,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm1"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"ttype1",
null,
@@ -114,9 +126,9 @@
}
@Test
- public void testWriteSdkSandboxAuditLogs_multiplePerms() {
- writeTestLog("denied", "perm1 perm2", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm3 perm4", "sdk_sandbox_audit", "ttype", "tclass");
+ public void testWriteAuditLogs_multiplePerms() {
+ writeTestLog("denied", "perm1 perm2", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm3 perm4", TEST_DOMAIN, "ttype", "tclass");
boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
@@ -127,7 +139,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm1", "perm2"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"ttype",
null,
@@ -140,7 +152,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm3", "perm4"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"ttype",
null,
@@ -150,11 +162,11 @@
}
@Test
- public void testWriteSdkSandboxAuditLogs_withPaths() {
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass", "/good/path");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass", "/very/long/path");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass", "/short_path");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass", "not_a_path");
+ public void testWriteAuditLogs_withPaths() {
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "/good/path");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "/very/long/path");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "/short_path");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "not_a_path");
boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
@@ -165,7 +177,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"ttype",
null,
@@ -178,7 +190,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"ttype",
null,
@@ -191,7 +203,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"ttype",
null,
@@ -204,7 +216,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"ttype",
null,
@@ -214,23 +226,14 @@
}
@Test
- public void testWriteSdkSandboxAuditLogs_withCategories() {
- writeTestLog(
- "denied", "perm", "sdk_sandbox_audit", new int[] {123}, "ttype", null, "tclass");
+ public void testWriteAuditLogs_withCategories() {
+ writeTestLog("denied", "perm", TEST_DOMAIN, new int[] {123}, "ttype", null, "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, new int[] {123, 456}, "ttype", null, "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, null, "ttype", new int[] {666}, "tclass");
writeTestLog(
"denied",
"perm",
- "sdk_sandbox_audit",
- new int[] {123, 456},
- "ttype",
- null,
- "tclass");
- writeTestLog(
- "denied", "perm", "sdk_sandbox_audit", null, "ttype", new int[] {666}, "tclass");
- writeTestLog(
- "denied",
- "perm",
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
new int[] {123, 456},
"ttype",
new int[] {666, 777},
@@ -245,7 +248,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
new int[] {123},
"ttype",
null,
@@ -258,7 +261,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
new int[] {123, 456},
"ttype",
null,
@@ -271,7 +274,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"ttype",
new int[] {666},
@@ -284,7 +287,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
new int[] {123, 456},
"ttype",
new int[] {666, 777},
@@ -294,11 +297,11 @@
}
@Test
- public void testWriteSdkSandboxAuditLogs_withPathAndCategories() {
+ public void testWriteAuditLogs_withPathAndCategories() {
writeTestLog(
"denied",
"perm",
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
new int[] {123},
"ttype",
new int[] {666},
@@ -314,7 +317,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
new int[] {123},
"ttype",
new int[] {666},
@@ -324,10 +327,10 @@
}
@Test
- public void testWriteSdkSandboxAuditLogs_permissive() {
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass", true);
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass", false);
+ public void testWriteAuditLogs_permissive() {
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", true);
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", false);
boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
@@ -338,7 +341,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"ttype",
null,
@@ -352,7 +355,7 @@
FrameworkStatsLog.SELINUX_AUDIT_LOG,
false,
new String[] {"perm"},
- "sdk_sandbox_audit",
+ TEST_DOMAIN,
null,
"ttype",
null,
@@ -362,7 +365,7 @@
}
@Test
- public void testNotWriteAuditLogs_notSdkSandbox() {
+ public void testNotWriteAuditLogs_notTestDomain() {
writeTestLog("denied", "perm", "stype", "ttype", "tclass");
boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
@@ -385,15 +388,15 @@
}
@Test
- public void testWriteSdkSandboxAuditLogs_upToQuota() {
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
+ public void testWriteAuditLogs_upToQuota() {
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
// These are not pushed.
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
@@ -415,14 +418,14 @@
}
@Test
- public void testWriteSdkSandboxAuditLogs_resetQuota() {
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
+ public void testWriteAuditLogs_resetQuota() {
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
assertThat(done).isTrue();
@@ -441,11 +444,11 @@
anyBoolean()),
times(5));
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
// move the clock forward to reset the quota limiter.
mClock.currentTimeMillis += Duration.ofHours(1).toMillis();
done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
@@ -468,16 +471,16 @@
@Test
public void testNotWriteAuditLogs_stopRequested() {
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
// These are not pushed.
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
- mSelinuxAutidLogsCollector.mStopRequested.set(true);
+ mSelinuxAutidLogsCollector.setStopRequested(true);
boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
assertThat(done).isFalse();
verify(
@@ -495,7 +498,7 @@
anyBoolean()),
never());
- mSelinuxAutidLogsCollector.mStopRequested.set(false);
+ mSelinuxAutidLogsCollector.setStopRequested(false);
done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
assertThat(done).isTrue();
verify(
@@ -516,8 +519,8 @@
@Test
public void testAuditLogs_resumeJobDoesNotExceedLimit() {
- writeTestLog("denied", "perm", "sdk_sandbox_audit", "ttype", "tclass");
- mSelinuxAutidLogsCollector.mStopRequested.set(true);
+ writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ mSelinuxAutidLogsCollector.setStopRequested(true);
boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
diff --git a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java
new file mode 100644
index 0000000..2aea8a0
--- /dev/null
+++ b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.selinux;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+public class SelinuxAuditLogsJobTest {
+
+ private final JobService mJobService = mock(JobService.class);
+ private final SelinuxAuditLogsCollector mAuditLogsCollector =
+ mock(SelinuxAuditLogsCollector.class);
+ private final JobParameters mParams = createJobParameters(666);
+ private final SelinuxAuditLogsJob mAuditLogsJob = new SelinuxAuditLogsJob(mAuditLogsCollector);
+
+ @Before
+ public void setUp() {
+ mAuditLogsCollector.mStopRequested = new AtomicBoolean();
+ }
+
+ @Test
+ public void testFinishSuccessfully() {
+ when(mAuditLogsCollector.collect(anyInt())).thenReturn(true);
+
+ mAuditLogsJob.start(mJobService, mParams);
+
+ verify(mJobService).jobFinished(mParams, /* wantsReschedule= */ false);
+ assertThat(mAuditLogsJob.isRunning()).isFalse();
+ }
+
+ @Test
+ public void testInterrupt() {
+ when(mAuditLogsCollector.collect(anyInt())).thenReturn(false);
+
+ mAuditLogsJob.start(mJobService, mParams);
+
+ verify(mJobService, never()).jobFinished(any(), anyBoolean());
+ assertThat(mAuditLogsJob.isRunning()).isFalse();
+ }
+
+ @Test
+ public void testInterruptAndResume() {
+ when(mAuditLogsCollector.collect(anyInt())).thenReturn(false);
+ mAuditLogsJob.start(mJobService, mParams);
+ verify(mJobService, never()).jobFinished(any(), anyBoolean());
+
+ when(mAuditLogsCollector.collect(anyInt())).thenReturn(true);
+ mAuditLogsJob.start(mJobService, mParams);
+ verify(mJobService).jobFinished(mParams, /* wantsReschedule= */ false);
+ assertThat(mAuditLogsJob.isRunning()).isFalse();
+ }
+
+ @Test
+ public void testRequestStop() throws InterruptedException {
+ Semaphore isRunning = new Semaphore(0);
+ Semaphore stopRequested = new Semaphore(0);
+ AtomicReference<Throwable> uncaughtException = new AtomicReference<>();
+
+ // Set up a logs collector that runs in a worker thread until a stop is requested.
+ when(mAuditLogsCollector.collect(anyInt()))
+ .thenAnswer(
+ invocation -> {
+ assertThat(mAuditLogsCollector.mStopRequested.get()).isFalse();
+ isRunning.release();
+ stopRequested.acquire();
+ assertThat(mAuditLogsCollector.mStopRequested.get()).isTrue();
+ return true;
+ });
+ Thread jobThread =
+ new Thread(
+ () -> {
+ mAuditLogsJob.start(mJobService, mParams);
+ });
+ jobThread.setUncaughtExceptionHandler(
+ (thread, exception) -> uncaughtException.set(exception));
+ assertThat(mAuditLogsJob.isRunning()).isFalse();
+ jobThread.start();
+
+ // Wait until the worker thread is running.
+ isRunning.acquire();
+ assertThat(mAuditLogsJob.isRunning()).isTrue();
+
+ // Request for the worker thread to stop, and wait to verify.
+ mAuditLogsJob.requestStop();
+ stopRequested.release();
+ jobThread.join();
+ assertThat(uncaughtException.get()).isNull();
+ assertThat(mAuditLogsJob.isRunning()).isFalse();
+ }
+
+ private static JobParameters createJobParameters(int jobId) {
+ JobParameters jobParameters = mock(JobParameters.class);
+ when(jobParameters.getJobId()).thenReturn(jobId);
+ return jobParameters;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 4e059b4..8024915 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -17,7 +17,6 @@
package com.android.server;
import static com.android.server.GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
-import static com.android.server.GestureLauncherService.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -773,6 +772,9 @@
@Test
public void testInterceptPowerKeyDown_triggerEmergency_fiveFastTaps_gestureIgnored() {
+ when(mResources.getInteger(
+ com.android.internal.R.integer.config_defaultMinEmergencyGestureTapDurationMillis))
+ .thenReturn(200);
// Trigger emergency by tapping button 5 times
long eventTime = triggerEmergencyGesture(/* tapIntervalMs= */ 1);
@@ -1449,7 +1451,7 @@
long emergencyGestureTapDetectionMinTimeMs = Settings.Global.getInt(
mContext.getContentResolver(),
Settings.Global.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS,
- EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS);
+ 200);
assertTrue(intercepted);
if (tapIntervalMs * 4 > emergencyGestureTapDetectionMinTimeMs) {
assertTrue(outLaunched.value);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 1bf9a9d..5a17851 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -69,10 +69,9 @@
import android.os.IBinder;
import android.os.LocaleList;
import android.os.UserHandle;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
@@ -144,8 +143,7 @@
ApplicationProvider.getApplicationContext());
@Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final int ACTION_ID = 20;
private static final String LABEL = "label";
@@ -624,7 +622,7 @@
@SmallTest
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ @EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testOnClientChange_magnificationTwoFingerTripleTapEnabled_requestConnection() {
when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false);
@@ -642,7 +640,7 @@
@SmallTest
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ @EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testOnClientChange_magnificationTwoFingerTripleTapDisabled_requestDisconnection() {
when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false);
@@ -704,7 +702,7 @@
@SmallTest
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ @EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void onClientChange_magnificationTwoFingerTripleTapDisabled_removeMagnificationButton() {
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
@@ -720,7 +718,7 @@
@SmallTest
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ @EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void onClientChange_magnificationTwoFingerTripleTapEnabled_keepMagnificationButton() {
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
@@ -772,7 +770,7 @@
@SmallTest
@Test
- @RequiresFlagsDisabled(com.android.systemui.Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
+ @DisableFlags(com.android.systemui.Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
public void testPerformAccessibilityShortcut_hearingAids_startActivityWithExpectedComponent() {
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
@@ -790,7 +788,7 @@
@SmallTest
@Test
- @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
+ @EnableFlags(com.android.systemui.Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
public void testPerformAccessibilityShortcut_hearingAids_sendExpectedBroadcast() {
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
@@ -949,7 +947,7 @@
}
@Test
- @RequiresFlagsEnabled(FLAG_SKIP_ACCESSIBILITY_WARNING_DIALOG_FOR_TRUSTED_SERVICES)
+ @EnableFlags(FLAG_SKIP_ACCESSIBILITY_WARNING_DIALOG_FOR_TRUSTED_SERVICES)
public void testIsAccessibilityServiceWarningRequired_notRequiredIfAllowlisted() {
mockManageAccessibilityGranted(mTestableContext);
final AccessibilityServiceInfo info_a = mockAccessibilityServiceInfo(
@@ -1008,6 +1006,33 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_HARDWARE_SHORTCUT_DISABLES_WARNING)
+ public void enableHardwareShortcutsForTargets_shortcutDialogSetting_isShown() {
+ Settings.Secure.putInt(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+ AccessibilityShortcutController.DialogStatus.NOT_SHOWN
+ );
+
+ mockManageAccessibilityGranted(mTestableContext);
+ setupShortcutTargetServices();
+ String target = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
+
+ mA11yms.enableShortcutsForTargets(
+ /* enable= */ true,
+ UserShortcutType.HARDWARE,
+ List.of(target),
+ mA11yms.getCurrentUserIdLocked());
+ mTestableLooper.processAllMessages();
+
+ assertThat(Settings.Secure.getInt(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+ AccessibilityShortcutController.DialogStatus.NOT_SHOWN))
+ .isEqualTo(AccessibilityShortcutController.DialogStatus.SHOWN);
+ }
+
+ @Test
public void enableShortcutsForTargets_disableSoftwareShortcut_shortcutTurnedOff()
throws Exception {
String target = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
@@ -1341,7 +1366,7 @@
}
@Test
- @RequiresFlagsEnabled(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_statusBarServiceNotGranted_throwsException() {
mTestableContext.getTestablePermissions().setPermission(
Manifest.permission.STATUS_BAR_SERVICE, PackageManager.PERMISSION_DENIED);
@@ -1355,7 +1380,7 @@
}
@Test
- @RequiresFlagsEnabled(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_manageAccessibilityNotGranted_throwsException() {
mockStatusBarServiceGranted(mTestableContext);
mTestableContext.getTestablePermissions().setPermission(
@@ -1369,7 +1394,7 @@
}
@Test
- @RequiresFlagsEnabled(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_qsTileChanges_updateA11yTilesInQsPanel() {
mockStatusBarServiceGranted(mTestableContext);
mockManageAccessibilityGranted(mTestableContext);
@@ -1389,7 +1414,7 @@
}
@Test
- @RequiresFlagsEnabled(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_sameQsTiles_noUpdateToA11yTilesInQsPanel() {
notifyQuickSettingsTilesChanged_qsTileChanges_updateA11yTilesInQsPanel();
List<ComponentName> tiles =
@@ -1406,7 +1431,7 @@
}
@Test
- @RequiresFlagsEnabled(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_serviceWarningRequired_qsShortcutRemainDisabled() {
mockStatusBarServiceGranted(mTestableContext);
mockManageAccessibilityGranted(mTestableContext);
@@ -1424,7 +1449,7 @@
}
@Test
- @RequiresFlagsEnabled(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_serviceWarningNotRequired_qsShortcutEnabled() {
mockStatusBarServiceGranted(mTestableContext);
mockManageAccessibilityGranted(mTestableContext);
@@ -1446,7 +1471,7 @@
}
@Test
- @RequiresFlagsEnabled(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_addFrameworkTile_qsShortcutEnabled() {
mockStatusBarServiceGranted(mTestableContext);
mockManageAccessibilityGranted(mTestableContext);
@@ -1469,7 +1494,7 @@
}
@Test
- @RequiresFlagsEnabled(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_removeFrameworkTile_qsShortcutDisabled() {
notifyQuickSettingsTilesChanged_addFrameworkTile_qsShortcutEnabled();
Set<ComponentName> qsTiles = mA11yms.getCurrentUserState().getA11yQsTilesInQsPanel();
@@ -1487,7 +1512,7 @@
}
@Test
- @RequiresFlagsEnabled(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
+ @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void restoreAccessibilityQsTargets_a11yQsTargetsRestored() {
String daltonizerTile =
AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
@@ -1510,7 +1535,7 @@
}
@Test
- @RequiresFlagsDisabled(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
+ @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void restoreAccessibilityQsTargets_a11yQsTargetsNotRestored() {
String daltonizerTile =
AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index ab36ba2..0c92abc 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -3194,6 +3194,78 @@
}
@SmallTest
+ public void testAccountsChangedBroadcastMarkedAccountAsVisibleThreeTimes() throws Exception {
+ unlockSystemUser();
+
+ HashMap<String, Integer> visibility = new HashMap<>();
+ visibility.put("testpackage1", AccountManager.VISIBILITY_VISIBLE);
+
+ addAccountRemovedReceiver("testpackage1");
+ mAms.registerAccountListener(
+ new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
+ "testpackage1");
+ mAms.addAccountExplicitlyWithVisibility(
+ AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+ /* password= */ "p11",
+ /* extras= */ null,
+ visibility,
+ /* callerPackage= */ null);
+
+ updateBroadcastCounters(2);
+ assertEquals(mVisibleAccountsChangedBroadcasts, 1);
+ assertEquals(mLoginAccountsChangedBroadcasts, 1);
+ assertEquals(mAccountRemovedBroadcasts, 0);
+
+ mAms.setAccountVisibility(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+ "testpackage1",
+ AccountManager.VISIBILITY_VISIBLE);
+ mAms.setAccountVisibility(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+ "testpackage1",
+ AccountManager.VISIBILITY_VISIBLE);
+
+ updateBroadcastCounters(2);
+ assertEquals(mVisibleAccountsChangedBroadcasts, 1);
+ assertEquals(mLoginAccountsChangedBroadcasts, 1);
+ assertEquals(mAccountRemovedBroadcasts, 0);
+ }
+
+ @SmallTest
+ public void testAccountsChangedBroadcastChangedVisibilityTwoTimes() throws Exception {
+ unlockSystemUser();
+
+ HashMap<String, Integer> visibility = new HashMap<>();
+ visibility.put("testpackage1", AccountManager.VISIBILITY_VISIBLE);
+
+ addAccountRemovedReceiver("testpackage1");
+ mAms.registerAccountListener(
+ new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
+ "testpackage1");
+ mAms.addAccountExplicitlyWithVisibility(
+ AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+ /* password= */ "p11",
+ /* extras= */ null,
+ visibility,
+ /* callerPackage= */ null);
+
+ updateBroadcastCounters(2);
+ assertEquals(mVisibleAccountsChangedBroadcasts, 1);
+ assertEquals(mLoginAccountsChangedBroadcasts, 1);
+ assertEquals(mAccountRemovedBroadcasts, 0);
+
+ mAms.setAccountVisibility(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+ "testpackage1",
+ AccountManager.VISIBILITY_NOT_VISIBLE);
+ mAms.setAccountVisibility(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+ "testpackage1",
+ AccountManager.VISIBILITY_VISIBLE);
+
+ updateBroadcastCounters(7);
+ assertEquals(mVisibleAccountsChangedBroadcasts, 3);
+ assertEquals(mLoginAccountsChangedBroadcasts, 3);
+ assertEquals(mAccountRemovedBroadcasts, 1);
+ }
+
+ @SmallTest
public void testRegisterAccountListenerCredentialsUpdate() throws Exception {
unlockSystemUser();
mAms.registerAccountListener(
diff --git a/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
index 472a82c..d5638e9 100644
--- a/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
@@ -57,8 +57,9 @@
private ResolveInfo mResolveInfo1 = new ResolveInfo();
private ResolveInfo mResolveInfo2 = new ResolveInfo();
- private final String mTestPkg1 = "testPkg1", mTestPkg2 = "testPkg2", mTestPkg3 = "testPkg3";
- private final String mMusicFxPkgName = "com.android.musicfx";
+ private final String mTestPkg1 = new String("testPkg1"), mTestPkg2 = new String("testPkg2"),
+ mTestPkg3 = new String("testPkg3"), mTestPkg1Equivalent = new String("testPkg1");
+ private final String mMusicFxPkgName = new String("com.android.musicfx");
private final int mTestUid1 = 1, mTestUid2 = 2, mTestUid3 = 3, mMusicFxUid = 78;
private final int mTestSession1 = 11, mTestSession2 = 22, mTestSession3 = 33;
@@ -191,7 +192,8 @@
public void testCloseBroadcastIntent() {
Log.i(TAG, "running testCloseBroadcastIntent");
- closeSessionWithResList(null, 0, 0, null, mTestSession1, mTestUid1);
+ closeSessionWithResList(null, 0 /* unbind */, 0 /* broadcast */, null /* packageName */,
+ mTestSession1, mTestUid1);
}
/**
@@ -225,8 +227,10 @@
public void testBroadcastIntentWithNoPackageAndNoBroadcastReceiver() {
Log.i(TAG, "running testBroadcastIntentWithNoPackageAndNoBroadcastReceiver");
- openSessionWithResList(mEmptyList, 0, 0, null, mTestSession1, mTestUid1);
- closeSessionWithResList(mEmptyList, 0, 0, null, mTestSession1, mTestUid1);
+ openSessionWithResList(mEmptyList, 0 /* bind */, 0 /* broadcast */, null /* packageName */,
+ mTestSession1, mTestUid1);
+ closeSessionWithResList(mEmptyList, 0 /* unbind */, 0 /* broadcast */,
+ null /* packageName */, mTestSession1, mTestUid1);
}
/**
@@ -236,26 +240,10 @@
public void testBroadcastIntentWithNoPackageAndOneBroadcastReceiver() {
Log.i(TAG, "running testBroadcastIntentWithNoPackageAndOneBroadcastReceiver");
- int broadcasts = 1, bind = 1, unbind = 1;
- openSessionWithResList(mSingleList, bind, broadcasts, null, mTestSession1, mTestUid1);
- broadcasts = broadcasts + 1;
- closeSessionWithResList(mSingleList, unbind, broadcasts, null, mTestSession1, mTestUid1);
-
- // repeat with different session ID
- broadcasts = broadcasts + 1;
- bind = bind + 1;
- unbind = unbind + 1;
- openSessionWithResList(mSingleList, bind, broadcasts, null, mTestSession2, mTestUid1);
- broadcasts = broadcasts + 1;
- closeSessionWithResList(mSingleList, unbind, broadcasts, null, mTestSession2, mTestUid1);
-
- // repeat with different UID
- broadcasts = broadcasts + 1;
- bind = bind + 1;
- unbind = unbind + 1;
- openSessionWithResList(mSingleList, bind, broadcasts, null, mTestSession1, mTestUid2);
- broadcasts = broadcasts + 1;
- closeSessionWithResList(mSingleList, unbind, broadcasts, null, mTestSession1, mTestUid2);
+ openSessionWithResList(mSingleList, 0 /* bind */, 0 /* broadcast */,
+ null /* packageName */, mTestSession1, mTestUid1);
+ closeSessionWithResList(mSingleList, 0 /* unbind */, 0 /* broadcast */,
+ null /* packageName */, mTestSession1, mTestUid1);
}
/**
@@ -265,8 +253,50 @@
public void testBroadcastIntentWithNoPackageAndTwoBroadcastReceivers() {
Log.i(TAG, "running testBroadcastIntentWithNoPackageAndTwoBroadcastReceivers");
- openSessionWithResList(mDoubleList, 1, 1, null, mTestSession1, mTestUid1);
- closeSessionWithResList(mDoubleList, 1, 2, null, mTestSession1, mTestUid1);
+ openSessionWithResList(mDoubleList, 0 /* bind */, 0 /* broadcast */,
+ null /* packageName */, mTestSession1, mTestUid1);
+ closeSessionWithResList(mDoubleList, 0 /* bind */, 0 /* broadcast */,
+ null /* packageName */, mTestSession1, mTestUid1);
+ }
+
+ @Test
+ public void testBroadcastIntentWithPackageAndOneBroadcastReceiver() {
+ Log.i(TAG, "running testBroadcastIntentWithPackageAndOneBroadcastReceiver");
+
+ int broadcasts = 1, bind = 1, unbind = 1;
+ openSessionWithResList(mSingleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+ mTestUid1);
+
+ // repeat with different session ID
+ broadcasts = broadcasts + 1;
+ bind = bind + 1;
+ unbind = unbind + 1;
+ openSessionWithResList(mSingleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid1);
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg2, mTestSession2,
+ mTestUid1);
+
+ // repeat with different UID
+ broadcasts = broadcasts + 1;
+ bind = bind + 1;
+ unbind = unbind + 1;
+ openSessionWithResList(mSingleList, bind, broadcasts, mTestPkg3, mTestSession1, mTestUid2);
+ broadcasts = broadcasts + 1;
+ closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg3, mTestSession1,
+ mTestUid2);
+ }
+
+ @Test
+ public void testBroadcastIntentWithPackageAndTwoBroadcastReceivers() {
+ Log.i(TAG, "running testBroadcastIntentWithPackageAndTwoBroadcastReceivers");
+
+ openSessionWithResList(mDoubleList, 1 /* bind */, 1 /* broadcast */,
+ mTestPkg1 /* packageName */, mTestSession1, mTestUid1);
+ closeSessionWithResList(mDoubleList, 1 /* unbind */, 2 /* broadcast */,
+ mTestPkg1 /* packageName */, mTestSession1, mTestUid1);
}
/**
@@ -639,4 +669,18 @@
unbind = unbind + 1;
sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid3, unbind, broadcasts);
}
+
+ /**
+ * Test audio session open/close with same package name value but different String object.
+ */
+ @Test
+ public void testSessionOpenCloseWithSamePackageNameValueButDiffObject() {
+ Log.i(TAG, "running testSessionOpenCloseWithSamePackageNameValueButDiffObject");
+ int broadcasts = 1;
+ openSessionWithResList(mSingleList, 1 /* bind */, broadcasts, mTestPkg1, mTestSession1,
+ mTestUid1);
+ closeSessionWithResList(mSingleList, 1 /* unbind */, broadcasts + 1, mTestPkg1Equivalent,
+ mTestSession1, mTestUid1);
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 7e04277..90b131a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -16,10 +16,10 @@
package com.android.server.biometrics;
-import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS;
@@ -65,8 +65,6 @@
import android.os.UserHandle;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -204,43 +202,7 @@
}
@Test
- @RequiresFlagsDisabled(com.android.server.biometrics.Flags.FLAG_DE_HIDL)
- public void testRegisterAuthenticator_registerAuthenticators() throws Exception {
- final int fingerprintId = 0;
- final int fingerprintStrength = 15;
-
- final int faceId = 1;
- final int faceStrength = 15;
-
- final String[] config = {
- // ID0:Fingerprint:Strong
- String.format("%d:2:%d", fingerprintId, fingerprintStrength),
- // ID2:Face:Convenience
- String.format("%d:8:%d", faceId, faceStrength)
- };
-
- when(mInjector.getConfiguration(any())).thenReturn(config);
-
- mAuthService = new AuthService(mContext, mInjector);
- mAuthService.onStart();
-
- verify(mFingerprintService).registerAuthenticators(mFingerprintPropsCaptor.capture());
- final FingerprintSensorPropertiesInternal fingerprintProp =
- mFingerprintPropsCaptor.getValue().get(0);
- assertEquals(fingerprintProp.sensorId, fingerprintId);
- assertEquals(fingerprintProp.sensorStrength,
- Utils.authenticatorStrengthToPropertyStrength(fingerprintStrength));
-
- verify(mFaceService).registerAuthenticators(mFacePropsCaptor.capture());
- final FaceSensorPropertiesInternal faceProp = mFacePropsCaptor.getValue().get(0);
- assertEquals(faceProp.sensorId, faceId);
- assertEquals(faceProp.sensorStrength,
- Utils.authenticatorStrengthToPropertyStrength(faceStrength));
- }
-
- @Test
- @RequiresFlagsEnabled(com.android.server.biometrics.Flags.FLAG_DE_HIDL)
- public void testRegisterAuthenticator_registerAuthenticatorsLegacy() throws RemoteException {
+ public void testRegisterAuthenticator_registerAuthenticators() throws RemoteException {
final int fingerprintId = 0;
final int fingerprintStrength = 15;
@@ -265,7 +227,7 @@
mFingerprintLooper.dispatchAll();
mFaceLooper.dispatchAll();
- verify(mFingerprintService).registerAuthenticatorsLegacy(
+ verify(mFingerprintService).registerAuthenticators(
mFingerprintSensorConfigurationsCaptor.capture());
final SensorProps[] fingerprintProp = mFingerprintSensorConfigurationsCaptor.getValue()
@@ -275,8 +237,7 @@
assertEquals(fingerprintProp[0].commonProps.sensorStrength,
Utils.authenticatorStrengthToPropertyStrength(fingerprintStrength));
- verify(mFaceService).registerAuthenticatorsLegacy(
- mFaceSensorConfigurationsCaptor.capture());
+ verify(mFaceService).registerAuthenticators(mFaceSensorConfigurationsCaptor.capture());
final android.hardware.biometrics.face.SensorProps[] faceProp =
mFaceSensorConfigurationsCaptor.getValue()
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 34092b6..48f1286 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -25,6 +25,7 @@
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED;
+import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_PAUSED;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED_UI_SHOWING;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_ERROR_PENDING_SYSUI;
@@ -289,6 +290,44 @@
}
@Test
+ public void testOnRejectionReceivedBeforeOnDialogAnimatedIn() throws RemoteException {
+ final int fingerprintId = 0;
+ final int faceId = 1;
+ setupFingerprint(fingerprintId, FingerprintSensorProperties.TYPE_REAR);
+ setupFace(faceId, false /* confirmationAlwaysRequired */,
+ mock(IBiometricAuthenticator.class));
+ final AuthSession session = createAuthSession(mSensors,
+ false /* checkDevicePolicyManager */,
+ Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
+ 0 /* operationId */,
+ 0 /* userId */);
+ session.goToInitialState();
+
+ for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+ assertThat(sensor.getSensorState()).isEqualTo(BiometricSensor.STATE_WAITING_FOR_COOKIE);
+ session.onCookieReceived(
+ session.mPreAuthInfo.eligibleSensors.get(sensor.id).getCookie());
+ }
+ assertThat(session.allCookiesReceived()).isTrue();
+ assertThat(session.getState()).isEqualTo(STATE_AUTH_STARTED);
+
+ final BiometricSensor faceSensor = session.mPreAuthInfo.eligibleSensors.get(faceId);
+ final BiometricSensor fingerprintSensor = session.mPreAuthInfo.eligibleSensors.get(
+ fingerprintId);
+ session.onAuthenticationRejected(faceId);
+
+ assertThat(faceSensor.getSensorState()).isEqualTo(BiometricSensor.STATE_CANCELING);
+ assertThat(session.getState()).isEqualTo(STATE_AUTH_PAUSED);
+
+ session.onDialogAnimatedIn(true);
+
+ assertThat(session.getState()).isEqualTo(STATE_AUTH_STARTED_UI_SHOWING);
+ assertThat(fingerprintSensor.getSensorState()).isEqualTo(
+ BiometricSensor.STATE_AUTHENTICATING);
+ }
+
+ @Test
public void testCancelReducesAppetiteForCookies() throws Exception {
setupFace(0 /* id */, false /* confirmationAlwaysRequired */,
mock(IBiometricAuthenticator.class));
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 5fd29c2..503ab8e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -76,7 +76,6 @@
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
@@ -90,7 +89,6 @@
import android.view.DisplayInfo;
import android.view.WindowManager;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -246,14 +244,8 @@
when(mInjector.getGateKeeperService()).thenReturn(mGateKeeperService);
when(mInjector.getNotificationLogger()).thenReturn(mNotificationLogger);
when(mGateKeeperService.getSecureUserId(anyInt())).thenReturn(42L);
-
- if (com.android.server.biometrics.Flags.deHidl()) {
- when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn(
- new Handler(TestableLooper.get(this).getLooper()));
- } else {
- when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn(
- new Handler(Looper.getMainLooper()));
- }
+ when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn(
+ new Handler(TestableLooper.get(this).getLooper()));
final String[] config = {
"0:2:15", // ID0:Fingerprint:Strong
@@ -2037,11 +2029,7 @@
}
private void waitForIdle() {
- if (com.android.server.biometrics.Flags.deHidl()) {
- TestableLooper.get(this).processAllMessages();
- } else {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
+ TestableLooper.get(this).processAllMessages();
}
private byte[] generateRandomHAT() {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
index c7300bb..960357f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
@@ -87,8 +87,12 @@
private ISessionListener mSessionListener;
@Mock
private WindowManager mWindowManager;
+ @Mock
+ private Consumer<OperationContext> mStartHalConsumer;
- private OperationContextExt mOpContext = new OperationContextExt(true);
+ private final FingerprintAuthenticateOptions mAuthenticateOptions =
+ new FingerprintAuthenticateOptions.Builder().build();
+ private final OperationContextExt mOpContext = new OperationContextExt(true);
private IBiometricContextListener mListener;
private BiometricContextProvider mProvider;
@@ -200,11 +204,11 @@
final List<Integer> actual = new ArrayList<>();
final List<Integer> expected = List.of(FoldState.FULLY_CLOSED, FoldState.FULLY_OPENED,
FoldState.UNKNOWN, FoldState.HALF_OPENED);
- mProvider.subscribe(mOpContext, ctx -> {
+ mProvider.subscribe(mOpContext, mStartHalConsumer, ctx -> {
assertThat(ctx).isSameInstanceAs(mOpContext.toAidlContext());
assertThat(mProvider.getFoldState()).isEqualTo(ctx.foldState);
actual.add(ctx.foldState);
- });
+ }, mAuthenticateOptions);
for (int v : expected) {
mListener.onFoldChanged(v);
@@ -228,11 +232,11 @@
AuthenticateOptions.DISPLAY_STATE_NO_UI,
AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN);
- mProvider.subscribe(mOpContext, ctx -> {
+ mProvider.subscribe(mOpContext, mStartHalConsumer, ctx -> {
assertThat(ctx).isSameInstanceAs(mOpContext.toAidlContext());
assertThat(mProvider.getDisplayState()).isEqualTo(ctx.displayState);
actual.add(ctx.displayState);
- });
+ }, mAuthenticateOptions);
for (int v : expected) {
mListener.onDisplayStateChanged(v);
@@ -250,11 +254,11 @@
public void testSubscribesToAod() throws RemoteException {
final List<Boolean> actual = new ArrayList<>();
- mProvider.subscribe(mOpContext, ctx -> {
+ mProvider.subscribe(mOpContext, mStartHalConsumer, ctx -> {
assertThat(ctx).isSameInstanceAs(mOpContext.toAidlContext());
assertThat(mProvider.isAod()).isEqualTo(ctx.isAod);
actual.add(ctx.isAod);
- });
+ }, mAuthenticateOptions);
for (int v : List.of(
AuthenticateOptions.DISPLAY_STATE_AOD,
@@ -273,10 +277,10 @@
public void testSubscribesToAwake() throws RemoteException {
final List<Boolean> actual = new ArrayList<>();
- mProvider.subscribe(mOpContext, ctx -> {
+ mProvider.subscribe(mOpContext, mStartHalConsumer, ctx -> {
assertThat(ctx).isSameInstanceAs(mOpContext.toAidlContext());
actual.add(mProvider.isAwake());
- });
+ }, mAuthenticateOptions);
for (int v : List.of(
AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN,
@@ -295,14 +299,15 @@
public void testSubscribesWithDifferentState() throws RemoteException {
final Consumer<OperationContext> nonEmptyConsumer = mock(Consumer.class);
mListener.onDisplayStateChanged(AuthenticateOptions.DISPLAY_STATE_AOD);
- mProvider.subscribe(mOpContext, nonEmptyConsumer);
- verify(nonEmptyConsumer).accept(same(mOpContext.toAidlContext()));
+ mProvider.subscribe(mOpContext, mStartHalConsumer, nonEmptyConsumer, mAuthenticateOptions);
+
+ assertThat(mOpContext.getDisplayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_AOD);
}
@Test
public void testUnsubscribes() throws RemoteException {
final Consumer<OperationContext> emptyConsumer = mock(Consumer.class);
- mProvider.subscribe(mOpContext, emptyConsumer);
+ mProvider.subscribe(mOpContext, mStartHalConsumer, emptyConsumer, mAuthenticateOptions);
mProvider.unsubscribe(mOpContext);
mListener.onDisplayStateChanged(AuthenticateOptions.DISPLAY_STATE_AOD);
@@ -311,7 +316,7 @@
mListener.onDisplayStateChanged(AuthenticateOptions.DISPLAY_STATE_UNKNOWN);
final Consumer<OperationContext> nonEmptyConsumer = mock(Consumer.class);
- mProvider.subscribe(mOpContext, nonEmptyConsumer);
+ mProvider.subscribe(mOpContext, mStartHalConsumer, nonEmptyConsumer, mAuthenticateOptions);
mListener.onDisplayStateChanged(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN);
mProvider.unsubscribe(mOpContext);
mListener.onDisplayStateChanged(AuthenticateOptions.DISPLAY_STATE_NO_UI);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 971323a..fc573d2 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -52,13 +52,12 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
-import android.testing.TestableLooper;
import android.util.Slog;
import androidx.annotation.NonNull;
@@ -66,7 +65,6 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.nano.BiometricSchedulerProto;
@@ -79,7 +77,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.HashMap;
@@ -90,7 +89,6 @@
@Presubmit
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class BiometricSchedulerTest {
private static final String TAG = "BiometricSchedulerTest";
@@ -105,6 +103,9 @@
@Rule
public final CheckFlagsRule mCheckFlagsRule =
DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
private BiometricScheduler<IFingerprint, ISession> mScheduler;
private IBinder mToken;
private int mCurrentUserId = UserHandle.USER_SYSTEM;
@@ -121,8 +122,10 @@
mUsersStoppedCount++;
mCurrentUserId = UserHandle.USER_NULL;
};
+ private TestLooper mLooper;
private boolean mStartOperationsFinish = true;
private int mStartUserClientCount = 0;
+
@Mock
private IBiometricService mBiometricService;
@Mock
@@ -140,44 +143,39 @@
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
mToken = new Binder();
+ mLooper = new TestLooper();
+
when(mAuthSessionCoordinator.getLockoutStateFor(anyInt(), anyInt())).thenReturn(
BIOMETRIC_SUCCESS);
when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
- if (Flags.deHidl()) {
- mScheduler = new BiometricScheduler<>(
- new Handler(TestableLooper.get(this).getLooper()),
- BiometricScheduler.SENSOR_TYPE_UNKNOWN,
- null /* gestureAvailabilityDispatcher */,
- mBiometricService,
- LOG_NUM_RECENT_OPERATIONS,
- () -> mCurrentUserId,
- new UserSwitchProvider<IFingerprint, ISession>() {
- @NonNull
- @Override
- public StopUserClient<ISession> getStopUserClient(int userId) {
- return new TestStopUserClient(mContext, () -> mSession, mToken, userId,
- TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
- mUserStoppedCallback, () -> mShouldFailStopUser);
- }
- @NonNull
- @Override
- public StartUserClient<IFingerprint, ISession> getStartUserClient(
- int newUserId) {
- mStartUserClientCount++;
- return new TestStartUserClient(mContext, () -> mFingerprint, mToken,
- newUserId, TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
- mUserStartedCallback, mStartOperationsFinish);
- }
- });
- } else {
- mScheduler = new BiometricScheduler<>(
- new Handler(TestableLooper.get(this).getLooper()),
- BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */,
- mBiometricService, LOG_NUM_RECENT_OPERATIONS);
- }
+ mScheduler = new BiometricScheduler<>(
+ new Handler(mLooper.getLooper()),
+ BiometricScheduler.SENSOR_TYPE_UNKNOWN,
+ null /* gestureAvailabilityDispatcher */,
+ mBiometricService,
+ LOG_NUM_RECENT_OPERATIONS,
+ () -> mCurrentUserId,
+ new UserSwitchProvider<IFingerprint, ISession>() {
+ @NonNull
+ @Override
+ public StopUserClient<ISession> getStopUserClient(int userId) {
+ return new TestStopUserClient(mContext, () -> mSession, mToken, userId,
+ TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
+ mUserStoppedCallback, () -> mShouldFailStopUser);
+ }
+
+ @NonNull
+ @Override
+ public StartUserClient<IFingerprint, ISession> getStartUserClient(
+ int newUserId) {
+ mStartUserClientCount++;
+ return new TestStartUserClient(mContext, () -> mFingerprint, mToken,
+ newUserId, TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
+ mUserStartedCallback, mStartOperationsFinish);
+ }
+ });
}
@Test
@@ -657,7 +655,6 @@
@Test
public void testClearBiometricQueue_clearsHungAuthOperation() {
// Creating a hung client
- final TestableLooper looper = TestableLooper.get(this);
final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */,
@@ -676,9 +673,9 @@
assertNotNull(mScheduler.mCurrentOperation);
assertEquals(0, mScheduler.getCurrentPendingCount());
- looper.moveTimeForward(10000);
+ mLooper.moveTimeForward(10000);
waitForIdle();
- looper.moveTimeForward(3000);
+ mLooper.moveTimeForward(3000);
waitForIdle();
// The hung client did not honor this operation, verify onError and authenticated
@@ -693,7 +690,6 @@
@Test
public void testAuthWorks_afterClearBiometricQueue() {
// Creating a hung client
- final TestableLooper looper = TestableLooper.get(this);
final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */,
@@ -714,10 +710,10 @@
waitForIdle();
// The watchdog should kick off the cancellation
- looper.moveTimeForward(10000);
+ mLooper.moveTimeForward(10000);
waitForIdle();
// After 10 seconds the HAL has 3 seconds to respond to a cancel
- looper.moveTimeForward(3000);
+ mLooper.moveTimeForward(3000);
waitForIdle();
// The hung client did not honor this operation, verify onError and authenticated
@@ -752,10 +748,10 @@
client2.getCallback().onClientFinished(client2, true);
waitForIdle();
- looper.moveTimeForward(10000);
+ mLooper.moveTimeForward(10000);
waitForIdle();
// After 10 seconds the HAL has 3 seconds to respond to a cancel
- looper.moveTimeForward(3000);
+ mLooper.moveTimeForward(3000);
waitForIdle();
//Asserting auth client passes
@@ -766,7 +762,6 @@
@Test
public void testClearBiometricQueue_doesNotClearOperationsWhenQueueNotStuck() {
//Creating clients
- final TestableLooper looper = TestableLooper.get(this);
final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */,
@@ -793,10 +788,10 @@
waitForIdle();
// The watchdog should kick off the cancellation
- looper.moveTimeForward(10000);
+ mLooper.moveTimeForward(10000);
waitForIdle();
// After 10 seconds the HAL has 3 seconds to respond to a cancel
- looper.moveTimeForward(3000);
+ mLooper.moveTimeForward(3000);
waitForIdle();
//Watchdog does not clear pending operations
@@ -855,7 +850,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void testScheduleOperation_whenNoUser() {
mCurrentUserId = UserHandle.USER_NULL;
@@ -871,7 +865,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void testScheduleOperation_whenNoUser_notStarted() {
mCurrentUserId = UserHandle.USER_NULL;
mStartOperationsFinish = false;
@@ -896,7 +889,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void testScheduleOperation_whenNoUser_notStarted_andReset() {
mCurrentUserId = UserHandle.USER_NULL;
mStartOperationsFinish = false;
@@ -923,7 +915,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void testScheduleOperation_whenSameUser() {
mCurrentUserId = 10;
@@ -940,7 +931,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void testScheduleOperation_whenDifferentUser() {
mCurrentUserId = 10;
@@ -961,7 +951,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void testStartUser_alwaysStartsNextOperation() {
mCurrentUserId = UserHandle.USER_NULL;
@@ -990,7 +979,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void testStartUser_failsClearsStopUserClient() {
mCurrentUserId = UserHandle.USER_NULL;
@@ -1033,7 +1021,7 @@
}
private void waitForIdle() {
- TestableLooper.get(this).processAllMessages();
+ mLooper.dispatchAll();
}
private static class TestAuthenticateOptions implements AuthenticateOptions {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
deleted file mode 100644
index dd5d826..0000000
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * Copyright (C) 2021 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.biometrics.sensors;
-
-import static android.testing.TestableLooper.RunWithLooper;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-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.content.Context;
-import android.hardware.biometrics.IBiometricService;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.UserHandle;
-import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.filters.SmallTest;
-
-import com.android.server.biometrics.Flags;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Supplier;
-
-@Presubmit
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class UserAwareBiometricSchedulerTest {
-
- private static final String TAG = "UserAwareBiometricSchedulerTest";
- private static final int TEST_SENSOR_ID = 0;
-
- @Rule
- public final MockitoRule mockito = MockitoJUnit.rule();
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
- private Handler mHandler;
- private UserAwareBiometricScheduler mScheduler;
- private final IBinder mToken = new Binder();
-
- @Mock
- private Context mContext;
- @Mock
- private IBiometricService mBiometricService;
- @Mock
- private BiometricLogger mBiometricLogger;
- @Mock
- private BiometricContext mBiometricContext;
-
- private boolean mShouldFailStopUser = false;
- private final StopUserClientShouldFail mStopUserClientShouldFail =
- () -> {
- return mShouldFailStopUser;
- };
- private final TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback();
- private final TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback();
- private int mCurrentUserId = UserHandle.USER_NULL;
- private boolean mStartOperationsFinish = true;
- private int mStartUserClientCount = 0;
-
- @Before
- public void setUp() {
- mShouldFailStopUser = false;
- mHandler = new Handler(TestableLooper.get(this).getLooper());
- mScheduler = new UserAwareBiometricScheduler(TAG,
- mHandler,
- BiometricScheduler.SENSOR_TYPE_UNKNOWN,
- null /* gestureAvailabilityDispatcher */,
- mBiometricService,
- () -> mCurrentUserId,
- new UserAwareBiometricScheduler.UserSwitchCallback() {
- @NonNull
- @Override
- public StopUserClient<?> getStopUserClient(int userId) {
- return new TestStopUserClient(mContext, Object::new, mToken, userId,
- TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
- mUserStoppedCallback, mStopUserClientShouldFail);
- }
-
- @NonNull
- @Override
- public StartUserClient<?, ?> getStartUserClient(int newUserId) {
- mStartUserClientCount++;
- return new TestStartUserClient(mContext, Object::new, mToken, newUserId,
- TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
- mUserStartedCallback, mStartOperationsFinish);
- }
- });
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void testScheduleOperation_whenNoUser() {
- mCurrentUserId = UserHandle.USER_NULL;
-
- final BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
- when(nextClient.getTargetUserId()).thenReturn(0);
-
- mScheduler.scheduleClientMonitor(nextClient);
- waitForIdle();
-
- assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(0);
- assertThat(mUserStartedCallback.mStartedUsers).containsExactly(0);
- verify(nextClient).start(any());
- }
-
- @Test
- public void testScheduleOperation_whenNoUser_notStarted() {
- mCurrentUserId = UserHandle.USER_NULL;
- mStartOperationsFinish = false;
-
- final BaseClientMonitor[] nextClients = new BaseClientMonitor[]{
- mock(BaseClientMonitor.class),
- mock(BaseClientMonitor.class),
- mock(BaseClientMonitor.class)
- };
- for (BaseClientMonitor client : nextClients) {
- when(client.getTargetUserId()).thenReturn(5);
- mScheduler.scheduleClientMonitor(client);
- waitForIdle();
- }
-
- assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(0);
- assertThat(mUserStartedCallback.mStartedUsers).isEmpty();
- assertThat(mStartUserClientCount).isEqualTo(1);
- for (BaseClientMonitor client : nextClients) {
- verify(client, never()).start(any());
- }
- }
-
- @Test
- public void testScheduleOperation_whenNoUser_notStarted_andReset() {
- mCurrentUserId = UserHandle.USER_NULL;
- mStartOperationsFinish = false;
-
- final BaseClientMonitor client = mock(BaseClientMonitor.class);
- when(client.getTargetUserId()).thenReturn(5);
- mScheduler.scheduleClientMonitor(client);
- waitForIdle();
-
- final TestStartUserClient startUserClient =
- (TestStartUserClient) mScheduler.mCurrentOperation.getClientMonitor();
- mScheduler.reset();
- assertThat(mScheduler.mCurrentOperation).isNull();
-
- final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation(
- mock(BaseClientMonitor.class), new ClientMonitorCallback() {});
- mScheduler.mCurrentOperation = fakeOperation;
- startUserClient.mCallback.onClientFinished(startUserClient, true);
- assertThat(fakeOperation).isSameInstanceAs(mScheduler.mCurrentOperation);
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void testScheduleOperation_whenSameUser() {
- mCurrentUserId = 10;
-
- BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
- when(nextClient.getTargetUserId()).thenReturn(mCurrentUserId);
-
- mScheduler.scheduleClientMonitor(nextClient);
-
- waitForIdle();
-
- verify(nextClient).start(any());
- assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(0);
- assertThat(mUserStartedCallback.mStartedUsers).isEmpty();
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void testScheduleOperation_whenDifferentUser() {
- mCurrentUserId = 10;
-
- final int nextUserId = 11;
- BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
- when(nextClient.getTargetUserId()).thenReturn(nextUserId);
-
- mScheduler.scheduleClientMonitor(nextClient);
-
- waitForIdle();
- assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(1);
-
- waitForIdle();
- assertThat(mUserStartedCallback.mStartedUsers).containsExactly(nextUserId);
-
- waitForIdle();
- verify(nextClient).start(any());
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void testStartUser_alwaysStartsNextOperation() {
- BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
- when(nextClient.getTargetUserId()).thenReturn(10);
-
- mScheduler.scheduleClientMonitor(nextClient);
-
- waitForIdle();
- verify(nextClient).start(any());
-
- // finish first operation
- mScheduler.getInternalCallback().onClientFinished(nextClient, true /* success */);
- waitForIdle();
-
- // schedule second operation but swap out the current operation
- // before it runs so that it's not current when it's completion callback runs
- nextClient = mock(BaseClientMonitor.class);
- when(nextClient.getTargetUserId()).thenReturn(11);
- mUserStartedCallback.mAfterStart = () -> mScheduler.mCurrentOperation = null;
- mScheduler.scheduleClientMonitor(nextClient);
-
- waitForIdle();
- verify(nextClient).start(any());
- assertThat(mUserStartedCallback.mStartedUsers).containsExactly(10, 11).inOrder();
- assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(1);
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void testStartUser_failsClearsStopUserClient() {
- // When a stop user client fails, check that mStopUserClient
- // is set to null to prevent the scheduler from getting stuck.
- BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
- when(nextClient.getTargetUserId()).thenReturn(10);
-
- mScheduler.scheduleClientMonitor(nextClient);
-
- waitForIdle();
- verify(nextClient).start(any());
-
- // finish first operation
- mScheduler.getInternalCallback().onClientFinished(nextClient, true /* success */);
- waitForIdle();
-
- // schedule second operation but swap out the current operation
- // before it runs so that it's not current when it's completion callback runs
- nextClient = mock(BaseClientMonitor.class);
- when(nextClient.getTargetUserId()).thenReturn(11);
- mUserStartedCallback.mAfterStart = () -> mScheduler.mCurrentOperation = null;
- mShouldFailStopUser = true;
- mScheduler.scheduleClientMonitor(nextClient);
-
- waitForIdle();
- assertThat(mUserStartedCallback.mStartedUsers).containsExactly(10, 11).inOrder();
- assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(0);
- assertThat(mScheduler.getStopUserClient()).isEqualTo(null);
- }
-
- private void waitForIdle() {
- TestableLooper.get(this).processAllMessages();
- }
-
- private class TestUserStoppedCallback implements StopUserClient.UserStoppedCallback {
- int mNumInvocations;
-
- @Override
- public void onUserStopped() {
- mNumInvocations++;
- mCurrentUserId = UserHandle.USER_NULL;
- }
- }
-
- private class TestUserStartedCallback implements StartUserClient.UserStartedCallback<Object> {
- final List<Integer> mStartedUsers = new ArrayList<>();
- Runnable mAfterStart = null;
-
- @Override
- public void onUserStarted(int newUserId, Object newObject, int halInterfaceVersion) {
- mStartedUsers.add(newUserId);
- mCurrentUserId = newUserId;
- if (mAfterStart != null) {
- mAfterStart.run();
- }
- }
- }
-
- private interface StopUserClientShouldFail {
- boolean shouldFail();
- }
-
- private class TestStopUserClient extends StopUserClient<Object> {
- private StopUserClientShouldFail mShouldFailClient;
- public TestStopUserClient(@NonNull Context context,
- @NonNull Supplier<Object> lazyDaemon, @Nullable IBinder token, int userId,
- int sensorId, @NonNull BiometricLogger logger,
- @NonNull BiometricContext biometricContext,
- @NonNull UserStoppedCallback callback, StopUserClientShouldFail shouldFail) {
- super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
- mShouldFailClient = shouldFail;
- }
-
- @Override
- protected void startHalOperation() {
-
- }
-
- @Override
- public void start(@NonNull ClientMonitorCallback callback) {
- super.start(callback);
- if (mShouldFailClient.shouldFail()) {
- getCallback().onClientFinished(this, false /* success */);
- // When the above fails, it means that the HAL has died, in this case we
- // need to ensure the UserSwitchCallback correctly returns the NULL user handle.
- mCurrentUserId = UserHandle.USER_NULL;
- } else {
- onUserStopped();
- }
- }
-
- @Override
- public void unableToStart() {
-
- }
- }
-
- private static class TestStartUserClient extends StartUserClient<Object, Object> {
- private final boolean mShouldFinish;
-
- ClientMonitorCallback mCallback;
-
- public TestStartUserClient(@NonNull Context context,
- @NonNull Supplier<Object> lazyDaemon, @Nullable IBinder token, int userId,
- int sensorId, @NonNull BiometricLogger logger,
- @NonNull BiometricContext biometricContext,
- @NonNull UserStartedCallback<Object> callback, boolean shouldFinish) {
- super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
- mShouldFinish = shouldFinish;
- }
-
- @Override
- protected void startHalOperation() {
-
- }
-
- @Override
- public void start(@NonNull ClientMonitorCallback callback) {
- super.start(callback);
-
- mCallback = callback;
- if (mShouldFinish) {
- mUserStartedCallback.onUserStarted(
- getTargetUserId(), new Object(), 1 /* halInterfaceVersion */);
- callback.onClientFinished(this, true /* success */);
- }
- }
-
- @Override
- public void unableToStart() {
-
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
index e4c56a7..1ca36a3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
@@ -147,11 +147,10 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
- public void registerAuthenticatorsLegacy_defaultOnly() throws Exception {
+ public void registerAuthenticators_defaultOnly() throws Exception {
initService();
- mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ mFaceService.mServiceWrapper.registerAuthenticators(mFaceSensorConfigurations);
waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT),
@@ -161,13 +160,13 @@
}
@Test
- @RequiresFlagsEnabled({Flags.FLAG_DE_HIDL, Flags.FLAG_FACE_VHAL_FEATURE})
+ @RequiresFlagsEnabled(Flags.FLAG_FACE_VHAL_FEATURE)
public void registerAuthenticatorsLegacy_virtualOnly() throws Exception {
initService();
Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1);
- mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ mFaceService.mServiceWrapper.registerAuthenticators(mFaceSensorConfigurations);
waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL),
@@ -176,13 +175,13 @@
}
@Test
- @RequiresFlagsEnabled({Flags.FLAG_DE_HIDL, Flags.FLAG_FACE_VHAL_FEATURE})
- public void registerAuthenticatorsLegacy_virtualFaceOnly() throws Exception {
+ @RequiresFlagsEnabled(Flags.FLAG_FACE_VHAL_FEATURE)
+ public void registerAuthenticators_virtualFaceOnly() throws Exception {
initService();
Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
Settings.Secure.BIOMETRIC_FACE_VIRTUAL_ENABLED, 1);
- mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ mFaceService.mServiceWrapper.registerAuthenticators(mFaceSensorConfigurations);
waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL),
@@ -191,13 +190,12 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
- public void registerAuthenticatorsLegacy_virtualAlwaysWhenNoOther() throws Exception {
+ public void registerAuthenticators_virtualAlwaysWhenNoOther() throws Exception {
mFaceSensorConfigurations = new FaceSensorConfigurations(false);
mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_VIRTUAL});
initService();
- mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ mFaceService.mServiceWrapper.registerAuthenticators(mFaceSensorConfigurations);
waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL),
@@ -210,7 +208,7 @@
FaceAuthenticateOptions faceAuthenticateOptions = new FaceAuthenticateOptions.Builder()
.build();
initService();
- mFaceService.mServiceWrapper.registerAuthenticators(List.of());
+ mFaceService.mServiceWrapper.registerAuthenticators(mFaceSensorConfigurations);
waitForRegistration();
final long operationId = 5;
@@ -230,7 +228,7 @@
R.string.config_keyguardComponent,
OP_PACKAGE_NAME);
initService();
- mFaceService.mServiceWrapper.registerAuthenticators(List.of());
+ mFaceService.mServiceWrapper.registerAuthenticators(mFaceSensorConfigurations);
waitForRegistration();
mFaceService.mServiceWrapper.detectFace(mToken, mFaceServiceReceiver,
faceAuthenticateOptions);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 84c3684..a556f52 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -50,8 +50,6 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -60,7 +58,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.OperationContextExt;
@@ -137,6 +134,8 @@
private ArgumentCaptor<Consumer<OperationContext>> mContextInjector;
@Captor
private ArgumentCaptor<Consumer<OperationContext>> mStartHalConsumerCaptor;
+ @Captor
+ private ArgumentCaptor<FaceAuthenticateOptions> mFaceAuthenticateOptionsCaptor;
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@@ -159,21 +158,26 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
public void authWithContext_v2() throws RemoteException {
final FaceAuthenticationClient client = createClient(2);
client.start(mCallback);
+ verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(),
+ mStartHalConsumerCaptor.capture(), mContextInjector.capture(),
+ mFaceAuthenticateOptionsCaptor.capture());
+
+ mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor.getValue().toAidlContext(
+ mFaceAuthenticateOptionsCaptor.getValue()));
InOrder order = inOrder(mHal, mBiometricContext);
order.verify(mBiometricContext).updateContext(
mOperationContextCaptor.capture(), anyBoolean());
final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext();
+
order.verify(mHal).authenticateWithContext(eq(OP_ID), same(aidlContext));
assertThat(aidlContext.wakeReason).isEqualTo(WAKE_REASON);
assertThat(aidlContext.authenticateReason.getFaceAuthenticateReason())
.isEqualTo(AUTH_REASON);
-
verify(mHal, never()).authenticate(anyLong());
}
@@ -200,30 +204,6 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void notifyHalWhenContextChanges() throws RemoteException {
- final FaceAuthenticationClient client = createClient();
- client.start(mCallback);
-
- final ArgumentCaptor<OperationContext> captor =
- ArgumentCaptor.forClass(OperationContext.class);
- verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture());
- OperationContext opContext = captor.getValue();
-
- // fake an update to the context
- verify(mBiometricContext).subscribe(
- mOperationContextCaptor.capture(), mContextInjector.capture());
- assertThat(opContext).isSameInstanceAs(
- mOperationContextCaptor.getValue().toAidlContext());
- mContextInjector.getValue().accept(opContext);
- verify(mHal).onContextChanged(same(opContext));
-
- client.stopHalOperation();
- verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue()));
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void subscribeContextAndStartHal() throws RemoteException {
final FaceAuthenticationClient client = createClient();
client.start(mCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index e626f73..fd3f054 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -20,7 +20,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
@@ -37,8 +36,6 @@
import android.os.RemoteException;
import android.os.Vibrator;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.TestableContext;
@@ -46,7 +43,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.OperationContextExt;
@@ -58,7 +54,6 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -124,49 +119,6 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void detectWithContext_v2() throws RemoteException {
- final FaceDetectClient client = createClient(2);
- client.start(mCallback);
-
- InOrder order = inOrder(mHal, mBiometricContext);
- order.verify(mBiometricContext).updateContext(
- mOperationContextCaptor.capture(), anyBoolean());
-
- final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext();
- order.verify(mHal).detectInteractionWithContext(same(aidlContext));
- assertThat(aidlContext.wakeReason).isEqualTo(WAKE_REASON);
- assertThat(aidlContext.authenticateReason.getFaceAuthenticateReason())
- .isEqualTo(AUTH_REASON);
-
- verify(mHal, never()).detectInteraction();
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void notifyHalWhenContextChanges() throws RemoteException {
- final FaceDetectClient client = createClient();
- client.start(mCallback);
-
- final ArgumentCaptor<OperationContext> captor =
- ArgumentCaptor.forClass(OperationContext.class);
- verify(mHal).detectInteractionWithContext(captor.capture());
- OperationContext opContext = captor.getValue();
-
- // fake an update to the context
- verify(mBiometricContext).subscribe(
- mOperationContextCaptor.capture(), mContextInjector.capture());
- assertThat(opContext).isSameInstanceAs(
- mOperationContextCaptor.getValue().toAidlContext());
- mContextInjector.getValue().accept(opContext);
- verify(mHal).onContextChanged(same(opContext));
-
- client.stopHalOperation();
- verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue()));
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void subscribeContextAndStartHal() throws RemoteException {
final FaceDetectClient client = createClient();
client.start(mCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
index 02363cd..d6b5789 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -24,7 +24,6 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
@@ -38,8 +37,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.TestableContext;
@@ -47,7 +44,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.OperationContextExt;
@@ -60,7 +56,6 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -124,45 +119,6 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void enrollWithContext_v2() throws RemoteException {
- final FaceEnrollClient client = createClient(2);
- client.start(mCallback);
-
- InOrder order = inOrder(mHal, mBiometricContext);
- order.verify(mBiometricContext).updateContext(
- mOperationContextCaptor.capture(), anyBoolean());
-
- final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext();
- order.verify(mHal).enrollWithContext(any(), anyByte(), any(), any(), same(aidlContext));
- verify(mHal, never()).enroll(any(), anyByte(), any(), any());
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void notifyHalWhenContextChanges() throws RemoteException {
- final FaceEnrollClient client = createClient(3);
- client.start(mCallback);
-
- final ArgumentCaptor<OperationContext> captor =
- ArgumentCaptor.forClass(OperationContext.class);
- verify(mHal).enrollWithContext(any(), anyByte(), any(), any(), captor.capture());
- OperationContext opContext = captor.getValue();
-
- // fake an update to the context
- verify(mBiometricContext).subscribe(
- mOperationContextCaptor.capture(), mContextInjector.capture());
- assertThat(opContext).isSameInstanceAs(
- mOperationContextCaptor.getValue().toAidlContext());
- mContextInjector.getValue().accept(opContext);
- verify(mHal).onContextChanged(same(opContext));
-
- client.stopHalOperation();
- verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue()));
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void subscribeContextAndStartHal() throws RemoteException {
final FaceEnrollClient client = createClient(3);
client.start(mCallback);
@@ -192,16 +148,6 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void enrollWithFaceOptions() throws RemoteException {
- final FaceEnrollClient client = createClient(4);
- client.start(mCallback);
-
- verify(mHal).enrollWithOptions(any());
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void enrollWithFaceOptionsAfterSubscribingContext() throws RemoteException {
final FaceEnrollClient client = createClient(4);
client.start(mCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
index 9eca93e..c4e51f8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -44,22 +44,18 @@
import android.hardware.face.HidlFaceSensorConfig;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.server.biometrics.BiometricHandlerProvider;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationStateListeners;
@@ -134,13 +130,8 @@
when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn(
mBiometricCallbackHandler);
when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
- if (Flags.deHidl()) {
- when(mBiometricHandlerProvider.getFaceHandler()).thenReturn(new Handler(
- mLooper.getLooper()));
- } else {
- when(mBiometricHandlerProvider.getFaceHandler()).thenReturn(new Handler(
- Looper.getMainLooper()));
- }
+ when(mBiometricHandlerProvider.getFaceHandler()).thenReturn(new Handler(
+ mLooper.getLooper()));
final SensorProps sensor1 = new SensorProps();
sensor1.commonProps = new CommonProps();
@@ -176,7 +167,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void testAddingHidlSensors() {
when(mResources.getIntArray(anyInt())).thenReturn(new int[]{});
when(mResources.getBoolean(anyInt())).thenReturn(false);
@@ -247,7 +237,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void testAuthenticateCallbackHandler() {
waitForIdle();
@@ -295,10 +284,6 @@
}
private void waitForIdle() {
- if (Flags.deHidl()) {
- mLooper.dispatchAll();
- } else {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
+ mLooper.dispatchAll();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index fe9cd43..6780e60 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -41,7 +41,6 @@
import androidx.test.filters.SmallTest;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
@@ -50,7 +49,6 @@
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
import com.android.server.biometrics.sensors.UserSwitchProvider;
import org.junit.Before;
@@ -75,12 +73,8 @@
@Mock
private ISession mSession;
@Mock
- private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback;
- @Mock
private UserSwitchProvider<IFace, ISession> mUserSwitchProvider;
@Mock
- private AidlResponseHandler.HardwareUnavailableCallback mHardwareUnavailableCallback;
- @Mock
private LockoutResetDispatcher mLockoutResetDispatcher;
@Mock
private BiometricLogger mBiometricLogger;
@@ -94,6 +88,8 @@
private BaseClientMonitor mClientMonitor;
@Mock
private AidlSession mCurrentSession;
+ @Mock
+ private AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
@@ -108,27 +104,17 @@
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
- if (Flags.deHidl()) {
- mScheduler = new BiometricScheduler<>(
- new Handler(mLooper.getLooper()),
- BiometricScheduler.SENSOR_TYPE_FACE,
- null /* gestureAvailabilityDispatcher */,
- mBiometricService,
- 2 /* recentOperationsLimit */,
- () -> USER_ID,
- mUserSwitchProvider);
- } else {
- mScheduler = new UserAwareBiometricScheduler<>(TAG,
- new Handler(mLooper.getLooper()),
- BiometricScheduler.SENSOR_TYPE_FACE,
- null /* gestureAvailabilityDispatcher */,
- mBiometricService,
- () -> USER_ID,
- mUserSwitchCallback);
- }
+ mScheduler = new BiometricScheduler<>(
+ new Handler(mLooper.getLooper()),
+ BiometricScheduler.SENSOR_TYPE_FACE,
+ null /* gestureAvailabilityDispatcher */,
+ mBiometricService,
+ 2 /* recentOperationsLimit */,
+ () -> USER_ID,
+ mUserSwitchProvider);
mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID,
mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- mHardwareUnavailableCallback);
+ mAidlResponseHandlerCallback);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
deleted file mode 100644
index 949d6ee..0000000
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
+++ /dev/null
@@ -1,220 +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.biometrics.sensors.face.hidl;
-
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.isA;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.hardware.biometrics.ComponentInfoInternal;
-import android.hardware.biometrics.SensorProperties;
-import android.hardware.face.FaceSensorProperties;
-import android.hardware.face.FaceSensorPropertiesInternal;
-import android.hardware.face.IFaceServiceReceiver;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.R;
-import com.android.server.biometrics.Flags;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.sensors.AuthenticationStateListeners;
-import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.BiometricStateCallback;
-import com.android.server.biometrics.sensors.LockoutResetDispatcher;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.time.Clock;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.IntStream;
-
-@Presubmit
-@SmallTest
-public class Face10Test {
-
- private static final String TAG = "Face10Test";
- private static final int SENSOR_ID = 1;
- private static final int USER_ID = 20;
- private static final float FRR_THRESHOLD = 0.2f;
-
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Mock
- private Context mContext;
- @Mock
- private UserManager mUserManager;
- @Mock
- private Resources mResources;
- @Mock
- private BiometricScheduler mScheduler;
- @Mock
- private BiometricContext mBiometricContext;
- @Mock
- private BiometricStateCallback mBiometricStateCallback;
- @Mock
- private AuthenticationStateListeners mAuthenticationStateListeners;
-
- private final Handler mHandler = new Handler(Looper.getMainLooper());
- private LockoutResetDispatcher mLockoutResetDispatcher;
- private com.android.server.biometrics.sensors.face.hidl.Face10 mFace10;
- private IBinder mBinder;
-
- private static void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
- when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
-
- when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1))
- .thenReturn(FRR_THRESHOLD);
-
- mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
-
- final int maxEnrollmentsPerUser = 1;
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- final boolean supportsFaceDetection = false;
- final boolean supportsSelfIllumination = false;
- final boolean resetLockoutRequiresChallenge = false;
- final FaceSensorPropertiesInternal sensorProps = new FaceSensorPropertiesInternal(SENSOR_ID,
- SensorProperties.STRENGTH_STRONG, maxEnrollmentsPerUser, componentInfo,
- FaceSensorProperties.TYPE_UNKNOWN, supportsFaceDetection, supportsSelfIllumination,
- resetLockoutRequiresChallenge);
-
- Face10.sSystemClock = Clock.fixed(
- Instant.ofEpochMilli(100), ZoneId.of("America/Los_Angeles"));
- mFace10 = new Face10(mContext, mBiometricStateCallback, mAuthenticationStateListeners,
- sensorProps, mLockoutResetDispatcher, mHandler, mScheduler, mBiometricContext);
- mBinder = new Binder();
- }
-
- private void tick(long seconds) {
- waitForIdle();
- Face10.sSystemClock = Clock.fixed(Instant.ofEpochSecond(
- Face10.sSystemClock.instant().getEpochSecond() + seconds),
- ZoneId.of("America/Los_Angeles"));
- }
-
- @Test
- public void getAuthenticatorId_doesNotCrashWhenIdNotFound() {
- assertEquals(0, mFace10.getAuthenticatorId(0 /* sensorId */, 111 /* userId */));
- waitForIdle();
- }
-
- @Test
- public void scheduleRevokeChallenge_doesNotCrash() {
- mFace10.scheduleRevokeChallenge(0 /* sensorId */, 0 /* userId */, mBinder, TAG,
- 0 /* challenge */);
- waitForIdle();
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void scheduleGenerateChallenge_cachesResult() {
- final IFaceServiceReceiver[] mocks = IntStream.range(0, 3)
- .mapToObj(i -> mock(IFaceServiceReceiver.class))
- .toArray(IFaceServiceReceiver[]::new);
- for (IFaceServiceReceiver mock : mocks) {
- mFace10.scheduleGenerateChallenge(SENSOR_ID, USER_ID, mBinder, mock, TAG);
- tick(10);
- }
- tick(120);
- mFace10.scheduleGenerateChallenge(
- SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
- waitForIdle();
-
- verify(mScheduler, times(2))
- .scheduleClientMonitor(isA(FaceGenerateChallengeClient.class), any());
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void scheduleRevokeChallenge_waitsUntilEmpty() {
- final long challenge = 22;
- final IFaceServiceReceiver[] mocks = IntStream.range(0, 3)
- .mapToObj(i -> mock(IFaceServiceReceiver.class))
- .toArray(IFaceServiceReceiver[]::new);
- for (IFaceServiceReceiver mock : mocks) {
- mFace10.scheduleGenerateChallenge(SENSOR_ID, USER_ID, mBinder, mock, TAG);
- tick(10);
- }
- for (IFaceServiceReceiver mock : mocks) {
- mFace10.scheduleRevokeChallenge(SENSOR_ID, USER_ID, mBinder, TAG, challenge);
- tick(10);
- }
- waitForIdle();
-
- verify(mScheduler).scheduleClientMonitor(isA(FaceRevokeChallengeClient.class), any());
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void scheduleRevokeChallenge_doesNotWaitForever() {
- mFace10.scheduleGenerateChallenge(
- SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
- mFace10.scheduleGenerateChallenge(
- SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
- tick(10000);
- mFace10.scheduleGenerateChallenge(
- SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
- mFace10.scheduleRevokeChallenge(
- SENSOR_ID, USER_ID, mBinder, TAG, 8 /* challenge */);
- waitForIdle();
-
- verify(mScheduler).scheduleClientMonitor(isA(FaceRevokeChallengeClient.class), any());
- }
-
- @Test
- public void halServiceDied_resetsScheduler() {
- // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
- // serviceDied directly.
- mFace10.serviceDied(0 /* cookie */);
- waitForIdle();
- verify(mScheduler).reset();
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
deleted file mode 100644
index ec08329..0000000
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2021 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.biometrics.sensors.face.hidl;
-
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.hardware.biometrics.face.V1_0.OptionalUint64;
-import android.hardware.face.IFaceServiceReceiver;
-import android.os.Binder;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.SmallTest;
-
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@Presubmit
-@SmallTest
-public class FaceGenerateChallengeClientTest {
-
- private static final String TAG = "FaceGenerateChallengeClientTest";
- private static final int USER_ID = 2;
- private static final int SENSOR_ID = 4;
- private static final long START_TIME = 5000;
- private static final long CHALLENGE = 200;
-
- private final Context mContext = ApplicationProvider.getApplicationContext();
-
- @Mock
- private IBiometricsFace mIBiometricsFace;
- @Mock
- private IFaceServiceReceiver mClientReceiver;
- @Mock
- private IFaceServiceReceiver mOtherReceiver;
- @Mock
- private ClientMonitorCallback mMonitorCallback;
- @Mock
- private BiometricLogger mBiometricLogger;
- @Mock
- private BiometricContext mBiometricContext;
-
- private FaceGenerateChallengeClient mClient;
-
- @Before
- public void setup() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- final OptionalUint64 challenge = new OptionalUint64();
- challenge.value = CHALLENGE;
- when(mIBiometricsFace.generateChallenge(anyInt())).thenReturn(challenge);
-
- mClient = new FaceGenerateChallengeClient(mContext, () -> mIBiometricsFace, new Binder(),
- new ClientMonitorCallbackConverter(mClientReceiver), USER_ID,
- TAG, SENSOR_ID, mBiometricLogger, mBiometricContext , START_TIME);
- }
-
- @Test
- public void getCreatedAt() {
- assertEquals(START_TIME, mClient.getCreatedAt());
- }
-
- @Test
- public void reuseResult_whenNotReady() throws Exception {
- mClient.reuseResult(mOtherReceiver);
- verify(mOtherReceiver, never()).onChallengeGenerated(anyInt(), anyInt(), anyInt());
- }
-
- @Test
- public void reuseResult_whenReady() throws Exception {
- mClient.start(mMonitorCallback);
- mClient.reuseResult(mOtherReceiver);
- verify(mOtherReceiver).onChallengeGenerated(eq(SENSOR_ID), eq(USER_ID), eq(CHALLENGE));
- }
-
- @Test
- public void reuseResult_whenReallyReady() throws Exception {
- mClient.reuseResult(mOtherReceiver);
- mClient.start(mMonitorCallback);
- verify(mOtherReceiver).onChallengeGenerated(eq(SENSOR_ID), eq(USER_ID), eq(CHALLENGE));
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java
index b5d73d2..44da431 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java
@@ -16,8 +16,8 @@
package com.android.server.biometrics.sensors.face.hidl;
-import static com.android.server.biometrics.sensors.face.hidl.FaceGenerateChallengeClient.CHALLENGE_TIMEOUT_SEC;
import static com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter.ENROLL_TIMEOUT_SEC;
+import static com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter.CHALLENGE_TIMEOUT_SEC;
import static com.google.common.truth.Truth.assertThat;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
index 9a8cd48..6126af5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
@@ -51,7 +51,6 @@
import android.os.Binder;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
@@ -64,7 +63,6 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
@@ -90,8 +88,6 @@
private static final int ID_VIRTUAL = 6;
private static final String NAME_DEFAULT = "default";
private static final String NAME_VIRTUAL = "virtual";
- private static final List<FingerprintSensorPropertiesInternal> HIDL_AUTHENTICATORS =
- List.of();
private static final String OP_PACKAGE_NAME = "FingerprintServiceTest/SystemUi";
@Rule
@@ -185,7 +181,7 @@
private void initServiceWithAndWait(String... aidlInstances) throws Exception {
initServiceWith(aidlInstances);
- mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
+ mService.mServiceWrapper.registerAuthenticators(mFingerprintSensorConfigurations);
waitForRegistration();
}
@@ -193,18 +189,7 @@
public void registerAuthenticators_defaultOnly() throws Exception {
initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
- mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
- waitForRegistration();
-
- verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any());
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
- public void registerAuthenticatorsLegacy_defaultOnly() throws Exception {
- initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
-
- mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
+ mService.mServiceWrapper.registerAuthenticators(mFingerprintSensorConfigurations);
waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any());
@@ -216,7 +201,7 @@
Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1);
- mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
+ mService.mServiceWrapper.registerAuthenticators(mFingerprintSensorConfigurations);
waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
@@ -228,20 +213,7 @@
Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
Settings.Secure.BIOMETRIC_FINGERPRINT_VIRTUAL_ENABLED, 1);
- mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
- waitForRegistration();
-
- verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
- public void registerAuthenticatorsLegacy_virtualOnly() throws Exception {
- initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
- Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
- Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1);
-
- mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
+ mService.mServiceWrapper.registerAuthenticators(mFingerprintSensorConfigurations);
waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
@@ -249,23 +221,12 @@
@Test
public void registerAuthenticators_virtualAlwaysWhenNoOther() throws Exception {
- initServiceWith(NAME_VIRTUAL);
-
- mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
- waitForRegistration();
-
- verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
- public void registerAuthenticatorsLegacy_virtualAlwaysWhenNoOther() throws Exception {
mFingerprintSensorConfigurations =
new FingerprintSensorConfigurations(true);
mFingerprintSensorConfigurations.addAidlSensors(new String[]{NAME_VIRTUAL});
initServiceWith(NAME_VIRTUAL);
- mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
+ mService.mServiceWrapper.registerAuthenticators(mFingerprintSensorConfigurations);
waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 7a77392..db9fe7f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -30,7 +30,6 @@
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.same;
@@ -42,7 +41,6 @@
import android.app.ActivityTaskManager;
import android.content.ComponentName;
import android.hardware.biometrics.BiometricManager;
-import android.hardware.biometrics.common.AuthenticateReason;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.ISession;
@@ -52,13 +50,10 @@
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -67,7 +62,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
@@ -84,7 +78,6 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -180,36 +173,18 @@
public void authNoContext_v1() throws RemoteException {
final FingerprintAuthenticationClient client = createClient(1);
client.start(mCallback);
- if (Flags.deHidl()) {
- verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(),
- mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any());
- mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor
- .getValue().toAidlContext());
- }
+
+ verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(),
+ mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any());
+
+ mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor
+ .getValue().toAidlContext());
verify(mHal).authenticate(eq(OP_ID));
verify(mHal, never()).authenticateWithContext(anyLong(), any());
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void authWithContext_v2() throws RemoteException {
- final FingerprintAuthenticationClient client = createClient(2);
- client.start(mCallback);
-
- InOrder order = inOrder(mHal, mBiometricContext);
- order.verify(mBiometricContext).updateContext(
- mOperationContextCaptor.capture(), anyBoolean());
-
- final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext();
- order.verify(mHal).authenticateWithContext(eq(OP_ID), same(aidlContext));
- assertThat(aidlContext.authenticateReason.getFingerprintAuthenticateReason())
- .isEqualTo(AuthenticateReason.Fingerprint.UNKNOWN);
-
- verify(mHal, never()).authenticate(anyLong());
- }
-
- @Test
public void pointerUp_v1() throws RemoteException {
final FingerprintAuthenticationClient client = createClient(1);
client.start(mCallback);
@@ -277,21 +252,18 @@
final FingerprintAuthenticationClient client = createClient();
client.start(mCallback);
- if (Flags.deHidl()) {
- verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(),
- mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any());
- mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor
- .getValue().toAidlContext());
- }
+
+ verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(),
+ mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any());
+
+ mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor
+ .getValue().toAidlContext());
final ArgumentCaptor<OperationContext> captor =
ArgumentCaptor.forClass(OperationContext.class);
verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture());
OperationContext opContext = captor.getValue();
- if (!Flags.deHidl()) {
- verify(mBiometricContext).subscribe(
- mOperationContextCaptor.capture(), mContextInjector.capture());
- }
+
assertThat(mOperationContextCaptor.getValue().toAidlContext())
.isSameInstanceAs(opContext);
@@ -326,12 +298,12 @@
when(mBiometricContext.isAod()).thenReturn(false);
final FingerprintAuthenticationClient client = createClient();
client.start(mCallback);
- if (Flags.deHidl()) {
- verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(),
- mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any());
- mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor
- .getValue().toAidlContext());
- }
+
+ verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(),
+ mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any());
+
+ mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor
+ .getValue().toAidlContext());
verify(mLuxProbe, isAwake ? times(1) : never()).enable();
}
@@ -342,21 +314,18 @@
when(mBiometricContext.isAod()).thenReturn(true);
final FingerprintAuthenticationClient client = createClient();
client.start(mCallback);
- if (Flags.deHidl()) {
- verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(),
- mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any());
- mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor
- .getValue().toAidlContext());
- }
+
+ verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(),
+ mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any());
+
+ mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor
+ .getValue().toAidlContext());
final ArgumentCaptor<OperationContext> captor =
ArgumentCaptor.forClass(OperationContext.class);
verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture());
OperationContext opContext = captor.getValue();
- if (!Flags.deHidl()) {
- verify(mBiometricContext).subscribe(
- mOperationContextCaptor.capture(), mContextInjector.capture());
- }
+
assertThat(opContext).isSameInstanceAs(
mOperationContextCaptor.getValue().toAidlContext());
@@ -380,30 +349,6 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void notifyHalWhenContextChanges() throws RemoteException {
- final FingerprintAuthenticationClient client = createClient();
- client.start(mCallback);
-
- final ArgumentCaptor<OperationContext> captor =
- ArgumentCaptor.forClass(OperationContext.class);
- verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture());
- OperationContext opContext = captor.getValue();
-
- // fake an update to the context
- verify(mBiometricContext).subscribe(
- mOperationContextCaptor.capture(), mContextInjector.capture());
- assertThat(opContext).isSameInstanceAs(
- mOperationContextCaptor.getValue().toAidlContext());
- mContextInjector.getValue().accept(opContext);
- verify(mHal).onContextChanged(same(opContext));
-
- client.stopHalOperation();
- verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue()));
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void subscribeContextAndStartHal() throws RemoteException {
final FingerprintAuthenticationClient client = createClient();
client.start(mCallback);
@@ -603,7 +548,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void testLockoutTracker_authFailed() throws RemoteException {
final FingerprintAuthenticationClient client = createClient(1 /* version */,
true /* allowBackgroundAuthentication */, mClientMonitorCallbackConverter,
@@ -658,8 +602,7 @@
null /* taskStackListener */,
mUdfpsOverlayController, mSideFpsController, mAuthenticationStateListeners,
allowBackgroundAuthentication,
- mSensorProps,
- new Handler(mLooper.getLooper()), 0 /* biometricStrength */, mClock,
+ mSensorProps, 0 /* biometricStrength */,
lockoutTracker) {
@Override
protected ActivityTaskManager getActivityTaskManager() {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
index 9edb8dd..6b8c3cd 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
@@ -21,13 +21,11 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.hardware.biometrics.common.AuthenticateReason;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
@@ -35,8 +33,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.TestableContext;
@@ -44,7 +40,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.OperationContextExt;
@@ -56,7 +51,6 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -119,49 +113,6 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void detectNoContext_v2() throws RemoteException {
- final FingerprintDetectClient client = createClient(2);
-
- client.start(mCallback);
-
- InOrder order = inOrder(mHal, mBiometricContext);
- order.verify(mBiometricContext).updateContext(
- mOperationContextCaptor.capture(), anyBoolean());
-
- final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext();
- order.verify(mHal).detectInteractionWithContext(same(aidlContext));
- assertThat(aidlContext.authenticateReason.getFingerprintAuthenticateReason())
- .isEqualTo(AuthenticateReason.Fingerprint.UNKNOWN);
-
- verify(mHal, never()).detectInteraction();
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void notifyHalWhenContextChanges() throws RemoteException {
- final FingerprintDetectClient client = createClient();
- client.start(mCallback);
-
- final ArgumentCaptor<OperationContext> captor =
- ArgumentCaptor.forClass(OperationContext.class);
- verify(mHal).detectInteractionWithContext(captor.capture());
- OperationContext opContext = captor.getValue();
-
- // fake an update to the context
- verify(mBiometricContext).subscribe(
- mOperationContextCaptor.capture(), mContextInjector.capture());
- assertThat(opContext).isSameInstanceAs(
- mOperationContextCaptor.getValue().toAidlContext());
- mContextInjector.getValue().accept(opContext);
- verify(mHal).onContextChanged(same(opContext));
-
- client.stopHalOperation();
- verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue()));
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void subscribeContextAndStartHal() throws RemoteException {
final FingerprintDetectClient client = createClient();
client.start(mCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index 916f696..d2e1c3c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -24,7 +24,6 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.times;
@@ -44,8 +43,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -54,7 +51,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
@@ -70,7 +66,6 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -156,21 +151,6 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void enrollWithContext_v2() throws RemoteException {
- final FingerprintEnrollClient client = createClient(2);
-
- client.start(mCallback);
-
- InOrder order = inOrder(mHal, mBiometricContext);
- order.verify(mBiometricContext).updateContext(
- mOperationContextCaptor.capture(), anyBoolean());
- order.verify(mHal).enrollWithContext(any(),
- same(mOperationContextCaptor.getValue().toAidlContext()));
- verify(mHal, never()).enroll(any());
- }
-
- @Test
public void pointerUp_v1() throws RemoteException {
final FingerprintEnrollClient client = createClient(1);
client.start(mCallback);
@@ -253,29 +233,6 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL)
- public void notifyHalWhenContextChanges() throws RemoteException {
- final FingerprintEnrollClient client = createClient();
- client.start(mCallback);
-
- final ArgumentCaptor<OperationContext> captor =
- ArgumentCaptor.forClass(OperationContext.class);
- verify(mHal).enrollWithContext(any(), captor.capture());
- OperationContext opContext = captor.getValue();
-
- // fake an update to the context
- verify(mBiometricContext).subscribe(
- mOperationContextCaptor.capture(), mContextInjector.capture());
- mContextInjector.getValue().accept(
- mOperationContextCaptor.getValue().toAidlContext());
- verify(mHal).onContextChanged(same(opContext));
-
- client.stopHalOperation();
- verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue()));
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void subscribeContextAndStartHal() throws RemoteException {
final FingerprintEnrollClient client = createClient();
client.start(mCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index 0a35037..1f288b2 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -47,21 +47,17 @@
import android.hardware.fingerprint.HidlFingerprintSensorConfig;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.server.biometrics.BiometricHandlerProvider;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationStateListeners;
@@ -136,13 +132,8 @@
when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn(
mBiometricCallbackHandler);
- if (Flags.deHidl()) {
- when(mBiometricHandlerProvider.getFingerprintHandler()).thenReturn(
- new Handler(mLooper.getLooper()));
- } else {
- when(mBiometricHandlerProvider.getFingerprintHandler()).thenReturn(
- new Handler(Looper.getMainLooper()));
- }
+ when(mBiometricHandlerProvider.getFingerprintHandler()).thenReturn(
+ new Handler(mLooper.getLooper()));
final SensorProps sensor1 = new SensorProps();
sensor1.commonProps = new CommonProps();
@@ -176,7 +167,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void testAddingHidlSensors() {
when(mResources.getIntArray(anyInt())).thenReturn(new int[]{});
when(mResources.getBoolean(anyInt())).thenReturn(false);
@@ -252,7 +242,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
public void testScheduleAuthenticate() {
waitForIdle();
@@ -302,10 +291,6 @@
}
private void waitForIdle() {
- if (Flags.deHidl()) {
- mLooper.dispatchAll();
- } else {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
+ mLooper.dispatchAll();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index b4c2ee8..698db2e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -42,7 +42,6 @@
import androidx.test.filters.SmallTest;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
@@ -51,7 +50,6 @@
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
import com.android.server.biometrics.sensors.UserSwitchProvider;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
@@ -77,12 +75,8 @@
@Mock
private ISession mSession;
@Mock
- private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback;
- @Mock
private UserSwitchProvider<IFingerprint, ISession> mUserSwitchProvider;
@Mock
- private AidlResponseHandler.HardwareUnavailableCallback mHardwareUnavailableCallback;
- @Mock
private LockoutResetDispatcher mLockoutResetDispatcher;
@Mock
private BiometricLogger mLogger;
@@ -100,6 +94,8 @@
private BaseClientMonitor mClientMonitor;
@Mock
private HandlerThread mThread;
+ @Mock
+ AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
@@ -115,27 +111,17 @@
when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
when(mThread.getLooper()).thenReturn(mLooper.getLooper());
- if (Flags.deHidl()) {
- mScheduler = new BiometricScheduler<>(
- new Handler(mLooper.getLooper()),
- BiometricScheduler.SENSOR_TYPE_FP_OTHER,
- null /* gestureAvailabilityDispatcher */,
- mBiometricService,
- 2 /* recentOperationsLimit */,
- () -> USER_ID,
- mUserSwitchProvider);
- } else {
- mScheduler = new UserAwareBiometricScheduler<>(TAG,
- new Handler(mLooper.getLooper()),
- BiometricScheduler.SENSOR_TYPE_FP_OTHER,
- null /* gestureAvailabilityDispatcher */,
- mBiometricService,
- () -> USER_ID,
- mUserSwitchCallback);
- }
+ mScheduler = new BiometricScheduler<>(
+ new Handler(mLooper.getLooper()),
+ BiometricScheduler.SENSOR_TYPE_FP_OTHER,
+ null /* gestureAvailabilityDispatcher */,
+ mBiometricService,
+ 2 /* recentOperationsLimit */,
+ () -> USER_ID,
+ mUserSwitchProvider);
mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID,
mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- mHardwareUnavailableCallback);
+ mAidlResponseHandlerCallback);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
deleted file mode 100644
index 0d3f192..0000000
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
+++ /dev/null
@@ -1,148 +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.biometrics.sensors.fingerprint.hidl;
-
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.hardware.biometrics.ComponentInfoInternal;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintSensorProperties;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.annotation.NonNull;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.R;
-import com.android.server.biometrics.log.BiometricContext;
-import com.android.server.biometrics.sensors.AuthenticationStateListeners;
-import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.BiometricStateCallback;
-import com.android.server.biometrics.sensors.LockoutResetDispatcher;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@Presubmit
-@SmallTest
-public class Fingerprint21Test {
-
- private static final String TAG = "Fingerprint21Test";
- private static final int SENSOR_ID = 1;
-
- @Mock
- private Context mContext;
- @Mock
- private Resources mResources;
- @Mock
- private UserManager mUserManager;
- @Mock
- Fingerprint21.HalResultController mHalResultController;
- @Mock
- private BiometricScheduler mScheduler;
- @Mock
- private AuthenticationStateListeners mAuthenticationStateListeners;
- @Mock
- private BiometricStateCallback mBiometricStateCallback;
- @Mock
- private BiometricContext mBiometricContext;
-
- private LockoutResetDispatcher mLockoutResetDispatcher;
- private Fingerprint21 mFingerprint21;
-
- private static void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
- when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
- when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getInteger(eq(R.integer.config_fingerprintMaxTemplatesPerUser)))
- .thenReturn(5);
-
- mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
-
- final int maxEnrollmentsPerUser = 1;
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- final boolean resetLockoutRequiresHardwareAuthToken = false;
- final FingerprintSensorPropertiesInternal sensorProps =
- new FingerprintSensorPropertiesInternal(SENSOR_ID,
- FingerprintSensorProperties.STRENGTH_WEAK, maxEnrollmentsPerUser,
- componentInfo, FingerprintSensorProperties.TYPE_UNKNOWN,
- resetLockoutRequiresHardwareAuthToken);
-
- mFingerprint21 = new TestableFingerprint21(mContext, mBiometricStateCallback,
- mAuthenticationStateListeners, sensorProps, mScheduler,
- new Handler(Looper.getMainLooper()), mLockoutResetDispatcher, mHalResultController,
- mBiometricContext);
- }
-
- @Test
- public void getAuthenticatorId_doesNotCrashWhenIdNotFound() {
- assertEquals(0, mFingerprint21.getAuthenticatorId(0 /* sensorId */, 111 /* userId */));
- waitForIdle();
- }
-
- @Test
- public void halServiceDied_resetsScheduler() {
- // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
- // serviceDied directly.
- mFingerprint21.serviceDied(0 /* cookie */);
- waitForIdle();
- verify(mScheduler).reset();
- }
-
- private static class TestableFingerprint21 extends Fingerprint21 {
-
- TestableFingerprint21(@NonNull Context context,
- @NonNull BiometricStateCallback biometricStateCallback,
- @NonNull AuthenticationStateListeners authenticationStateListeners,
- @NonNull FingerprintSensorPropertiesInternal sensorProps,
- @NonNull BiometricScheduler scheduler, @NonNull Handler handler,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull HalResultController controller,
- @NonNull BiometricContext biometricContext) {
- super(context, biometricStateCallback, authenticationStateListeners, sensorProps,
- scheduler, handler, lockoutResetDispatcher, controller, biometricContext);
- }
-
- @Override
- synchronized IBiometricsFingerprint getDaemon() {
- return mock(IBiometricsFingerprint.class);
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
index 74e854e4..00c8ed1 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
@@ -53,7 +53,7 @@
private IInputDevicesChangedListener mDevicesChangedListener;
private final Map<String /* uniqueId */, Integer /* displayId */> mDisplayIdMapping =
new HashMap<>();
- private final Map<String /* phys */, String /* uniqueId */> mUniqueIdAssociation =
+ private final Map<String /* phys */, String /* uniqueId */> mUniqueIdAssociationByPort =
new HashMap<>();
InputManagerMockHelper(TestableLooper testableLooper,
@@ -79,10 +79,11 @@
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
doAnswer(inv -> mDevices.get(inv.getArgument(0)))
.when(mIInputManagerMock).getInputDevice(anyInt());
- doAnswer(inv -> mUniqueIdAssociation.put(inv.getArgument(0), inv.getArgument(1))).when(
- mIInputManagerMock).addUniqueIdAssociation(anyString(), anyString());
- doAnswer(inv -> mUniqueIdAssociation.remove(inv.getArgument(0))).when(
- mIInputManagerMock).removeUniqueIdAssociation(anyString());
+ doAnswer(inv -> mUniqueIdAssociationByPort.put(inv.getArgument(0),
+ inv.getArgument(1))).when(mIInputManagerMock).addUniqueIdAssociationByPort(
+ anyString(), anyString());
+ doAnswer(inv -> mUniqueIdAssociationByPort.remove(inv.getArgument(0))).when(
+ mIInputManagerMock).removeUniqueIdAssociationByPort(anyString());
// Set a new instance of InputManager for testing that uses the IInputManager mock as the
// interface to the server.
@@ -112,7 +113,7 @@
.setDescriptor(phys)
.setExternal(true)
.setAssociatedDisplayId(
- mDisplayIdMapping.getOrDefault(mUniqueIdAssociation.get(phys),
+ mDisplayIdMapping.getOrDefault(mUniqueIdAssociationByPort.get(phys),
Display.INVALID_DISPLAY))
.build();
diff --git a/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING
index 861562d..305108e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING
+++ b/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING
@@ -1,31 +1,11 @@
{
"presubmit": [
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.pm."
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- }
- ]
+ "name": "FrameworksServicesTests_pm_presubmit"
}
],
"postsubmit": [
{
- // Presubmit is intentional here while testing with SLO checker.
- // Tests are flaky, waiting to bypass.
- "name": "FrameworksServicesTests_pm_presubmit"
- },
- {
- // Leave postsubmit here when migrating
"name": "FrameworksServicesTests_pm_postsubmit"
}
]
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 4405a20..b91ef7c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -1256,6 +1256,30 @@
@MediumTest
@Test
+ public void testDefaultUserRestrictionsForPrivateProfile() {
+ assumeTrue(mUserManager.canAddPrivateProfile());
+ final int currentUserId = ActivityManager.getCurrentUser();
+ UserInfo privateProfileInfo = null;
+ try {
+ privateProfileInfo = createProfileForUser("Private",
+ UserManager.USER_TYPE_PROFILE_PRIVATE, currentUserId);
+ assertThat(privateProfileInfo).isNotNull();
+ } catch (Exception e) {
+ fail("Creation of private profile failed due to " + e.getMessage());
+ }
+ assertDefaultPrivateProfileRestrictions(privateProfileInfo.getUserHandle());
+ }
+
+ private void assertDefaultPrivateProfileRestrictions(UserHandle userHandle) {
+ Bundle defaultPrivateProfileRestrictions =
+ UserTypeFactory.getDefaultPrivateProfileRestrictions();
+ for (String restriction : defaultPrivateProfileRestrictions.keySet()) {
+ assertThat(mUserManager.hasUserRestrictionForUser(restriction, userHandle)).isTrue();
+ }
+ }
+
+ @MediumTest
+ @Test
public void testAddRestrictedProfile() throws Exception {
if (isAutomotive() || UserManager.isHeadlessSystemUserMode()) return;
assertWithMessage("There should be no associated restricted profiles before the test")
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 515898a..e6cf0c38 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -50,6 +50,7 @@
"SettingsLib",
"libprotobuf-java-lite",
"platformprotoslite",
+ "platform-parametric-runner-lib",
],
libs: [
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index ad25d76..770712a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -26,14 +26,18 @@
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
import static android.media.AudioAttributes.USAGE_UNKNOWN;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+import static com.android.server.notification.NotificationChannelExtractor.RESTRICT_AUDIO_ATTRIBUTES;
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.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.Flags;
@@ -43,12 +47,14 @@
import android.app.Person;
import android.media.AudioAttributes;
import android.net.Uri;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
+import com.android.internal.compat.IPlatformCompat;
import com.android.server.UiServiceTestCase;
import org.junit.Before;
@@ -60,6 +66,8 @@
public class NotificationChannelExtractorTest extends UiServiceTestCase {
@Mock RankingConfig mConfig;
+ @Mock
+ IPlatformCompat mPlatformCompat;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@@ -73,6 +81,7 @@
mExtractor = new NotificationChannelExtractor();
mExtractor.setConfig(mConfig);
mExtractor.initialize(mContext, null);
+ mExtractor.setCompatChangeLogger(mPlatformCompat);
}
private NotificationRecord getRecord(NotificationChannel channel, Notification n) {
@@ -82,7 +91,7 @@
}
@Test
- public void testExtractsUpdatedConversationChannel() {
+ public void testExtractsUpdatedConversationChannel() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
final Notification n = new Notification.Builder(getContext())
.setContentTitle("foo")
@@ -101,7 +110,7 @@
}
@Test
- public void testInvalidShortcutFlagEnabled_looksUpCorrectNonChannel() {
+ public void testInvalidShortcutFlagEnabled_looksUpCorrectNonChannel() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
final Notification n = new Notification.Builder(getContext())
.setContentTitle("foo")
@@ -122,7 +131,7 @@
}
@Test
- public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() {
+ public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
final Notification n = new Notification.Builder(getContext())
.setContentTitle("foo")
@@ -143,7 +152,7 @@
@Test
@EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_CALL)
- public void testAudioAttributes_callStyleCanUseCallUsage() {
+ public void testAudioAttributes_callStyleCanUseCallUsage() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
.setUsage(USAGE_NOTIFICATION_RINGTONE)
@@ -162,11 +171,12 @@
assertThat(mExtractor.process(r)).isNull();
assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION_RINGTONE);
assertThat(r.getChannel()).isEqualTo(channel);
+ verify(mPlatformCompat, never()).reportChangeByUid(anyLong(), anyInt());
}
@Test
@EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_CALL)
- public void testAudioAttributes_nonCallStyleCannotUseCallUsage() {
+ public void testAudioAttributes_nonCallStyleCannotUseCallUsage() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
.setUsage(USAGE_NOTIFICATION_RINGTONE)
@@ -180,13 +190,14 @@
assertThat(mExtractor.process(r)).isNull();
// instance updated
assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION);
+ verify(mPlatformCompat).reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, r.getUid());
// in-memory channel unchanged
assertThat(channel.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION_RINGTONE);
}
@Test
@EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_ALARM)
- public void testAudioAttributes_alarmCategoryCanUseAlarmUsage() {
+ public void testAudioAttributes_alarmCategoryCanUseAlarmUsage() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
.setUsage(USAGE_ALARM)
@@ -201,11 +212,12 @@
assertThat(mExtractor.process(r)).isNull();
assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_ALARM);
assertThat(r.getChannel()).isEqualTo(channel);
+ verify(mPlatformCompat, never()).reportChangeByUid(anyLong(), anyInt());
}
@Test
@EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_ALARM)
- public void testAudioAttributes_nonAlarmCategoryCannotUseAlarmUsage() {
+ public void testAudioAttributes_nonAlarmCategoryCannotUseAlarmUsage() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
.setUsage(USAGE_ALARM)
@@ -219,13 +231,14 @@
assertThat(mExtractor.process(r)).isNull();
// instance updated
assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION);
+ verify(mPlatformCompat).reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, r.getUid());
// in-memory channel unchanged
assertThat(channel.getAudioAttributes().getUsage()).isEqualTo(USAGE_ALARM);
}
@Test
@EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_MEDIA)
- public void testAudioAttributes_noMediaUsage() {
+ public void testAudioAttributes_noMediaUsage() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
.setUsage(USAGE_MEDIA)
@@ -239,13 +252,14 @@
assertThat(mExtractor.process(r)).isNull();
// instance updated
assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION);
+ verify(mPlatformCompat).reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, r.getUid());
// in-memory channel unchanged
assertThat(channel.getAudioAttributes().getUsage()).isEqualTo(USAGE_MEDIA);
}
@Test
@EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_MEDIA)
- public void testAudioAttributes_noUnknownUsage() {
+ public void testAudioAttributes_noUnknownUsage() throws RemoteException {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
.setUsage(USAGE_UNKNOWN)
@@ -259,6 +273,7 @@
assertThat(mExtractor.process(r)).isNull();
// instance updated
assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION);
+ verify(mPlatformCompat).reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, r.getUid());
// in-memory channel unchanged
assertThat(channel.getAudioAttributes().getUsage()).isEqualTo(USAGE_UNKNOWN);
}
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 6106278..011f2e3 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -106,22 +106,23 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-
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;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
+import static com.android.server.notification.Flags.FLAG_ALL_NOTIFS_NEED_TTL;
import static com.android.server.notification.NotificationManagerService.BITMAP_DURATION;
import static com.android.server.notification.NotificationManagerService.DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
import static com.android.server.notification.NotificationManagerService.NOTIFICATION_TTL;
+import static com.android.server.notification.NotificationManagerService.TAG;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED;
-
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -130,7 +131,6 @@
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
-
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.isNull;
@@ -138,10 +138,24 @@
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.*;
-
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singletonList;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+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;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
import android.Manifest;
import android.annotation.Nullable;
@@ -168,6 +182,8 @@
import android.app.RemoteInputHistoryItem;
import android.app.StatsManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.job.JobScheduler;
+import android.app.role.RoleManager;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
@@ -184,9 +200,11 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
+import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
@@ -217,6 +235,7 @@
import android.permission.PermissionManager;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.rule.LimitDevicesRule;
import android.provider.DeviceConfig;
@@ -235,7 +254,8 @@
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
import android.telecom.TelecomManager;
-import android.testing.AndroidTestingRunner;
+import android.testing.TestWithLooperRule;
+import android.testing.TestableContentResolver;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestablePermissions;
@@ -245,13 +265,13 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.Log;
import android.util.Pair;
import android.util.Xml;
+import android.view.accessibility.AccessibilityManager;
import android.widget.RemoteViews;
-
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-
import com.android.internal.R;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.config.sysui.TestableFlagResolver;
@@ -259,6 +279,7 @@
import com.android.internal.logging.InstanceIdSequenceFake;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.internal.widget.LockPatternUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.DeviceIdleInternal;
@@ -282,13 +303,10 @@
import com.android.server.utils.quota.MultiRateLimiter;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
-
import com.google.android.collect.Lists;
import com.google.common.collect.ImmutableList;
-
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
-
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -306,6 +324,8 @@
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -322,9 +342,9 @@
import java.util.function.Consumer;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
+@RunWith(ParameterizedAndroidJunit4.class)
@RunWithLooper
+@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
public class NotificationManagerServiceTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
private static final String TEST_PACKAGE = "The.name.is.Package.Test.Package";
@@ -369,6 +389,8 @@
@Mock
private PermissionHelper mPermissionHelper;
private NotificationChannelLoggerFake mLogger = new NotificationChannelLoggerFake();
+ @Rule(order = Integer.MAX_VALUE)
+ public TestWithLooperRule mlooperRule = new TestWithLooperRule();
private TestableLooper mTestableLooper;
@Mock
private RankingHelper mRankingHelper;
@@ -415,8 +437,8 @@
private final TestPostNotificationTrackerFactory mPostNotificationTrackerFactory =
new TestPostNotificationTrackerFactory();
- @Mock
- IIntentSender pi1;
+ private PendingIntent mActivityIntent;
+ private PendingIntent mActivityIntentImmutable;
private static final int MAX_POST_DELAY = 1000;
@@ -429,6 +451,9 @@
private static final String VALID_CONVO_SHORTCUT_ID = "shortcut";
private static final String SEARCH_SELECTOR_PKG = "searchSelector";
+ private static final String ADSERVICES_MODULE_PKG = "com.android.adservices";
+ private static final String ADSERVICES_APK_PKG = "com.android.adservices.api";
+
@Mock
private NotificationListeners mListeners;
@Mock
@@ -465,6 +490,7 @@
StatsManager mStatsManager;
@Mock
AlarmManager mAlarmManager;
+ @Mock JobScheduler mJobScheduler;
@Mock
MultiRateLimiter mToastRateLimiter;
BroadcastReceiver mPackageIntentReceiver;
@@ -508,6 +534,16 @@
}
}
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(
+ FLAG_ALL_NOTIFS_NEED_TTL);
+ }
+
+ public NotificationManagerServiceTest(FlagsParameterization flags) {
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
// Shell permisssions will override permissions of our app, so add all necessary permissions
@@ -540,12 +576,22 @@
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
LocalServices.removeServiceForTest(PermissionPolicyInternal.class);
LocalServices.addService(PermissionPolicyInternal.class, mPermissionPolicyInternal);
+ LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+ LocalServices.addService(ShortcutServiceInternal.class, mShortcutServiceInternal);
mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
mContext.addMockSystemService(NotificationManager.class, mMockNm);
+ mContext.addMockSystemService(RoleManager.class, mock(RoleManager.class));
+ mContext.addMockSystemService(Context.LAUNCHER_APPS_SERVICE, mLauncherApps);
+ mContext.addMockSystemService(Context.USER_SERVICE, mUm);
+ mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE,
+ mock(AccessibilityManager.class));
doNothing().when(mContext).sendBroadcast(any(), anyString());
doNothing().when(mContext).sendBroadcastAsUser(any(), any());
doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
+ TestableContentResolver cr = mock(TestableContentResolver.class);
+ when(mContext.getContentResolver()).thenReturn(cr);
+ doNothing().when(cr).registerContentObserver(any(), anyBoolean(), any(), anyInt());
setDpmAppOppsExemptFromDismissal(false);
@@ -648,11 +694,16 @@
});
// TODO (b/291907312): remove feature flag
- // NOTE: Prefer using the @EnableFlag annotation where possible. Do not add any android.app
+ // NOTE: Prefer using the @EnableFlags annotation where possible. Do not add any android.app
// flags here.
mSetFlagsRule.disableFlags(
Flags.FLAG_POLITE_NOTIFICATIONS, Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE);
+ mActivityIntent = spy(PendingIntent.getActivity(mContext, 0,
+ new Intent().setPackage(mPkg), PendingIntent.FLAG_MUTABLE));
+ mActivityIntentImmutable = spy(PendingIntent.getActivity(mContext, 0,
+ new Intent().setPackage(mPkg), FLAG_IMMUTABLE));
+
initNMS();
}
@@ -689,10 +740,15 @@
mPowerManager, mPostNotificationTrackerFactory);
mService.setAttentionHelper(mAttentionHelper);
+ mService.setLockPatternUtils(mock(LockPatternUtils.class));
// Return first true for RoleObserver main-thread check
when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
-
+ ModuleInfo moduleInfo = new ModuleInfo();
+ moduleInfo.setApexModuleName(ADSERVICES_MODULE_PKG);
+ moduleInfo.setApkInApexPackageNames(List.of(ADSERVICES_APK_PKG));
+ when(mPackageManagerClient.getInstalledModules(anyInt()))
+ .thenReturn(List.of(moduleInfo));
if (upToBootPhase >= SystemService.PHASE_SYSTEM_SERVICES_READY) {
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
}
@@ -749,7 +805,10 @@
}
assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
assertNotNull("User-switch receiver should exist", mUserSwitchIntentReceiver);
- assertNotNull("Notification timeout receiver should exist", mNotificationTimeoutReceiver);
+ if (!Flags.allNotifsNeedTtl()) {
+ assertNotNull("Notification timeout receiver should exist",
+ mNotificationTimeoutReceiver);
+ }
// Pretend the shortcut exists
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
@@ -834,9 +893,17 @@
if (mFile != null) mFile.delete();
clearDeviceConfig();
+ if (mActivityIntent != null) {
+ mActivityIntent.cancel();
+ }
+
+ mService.clearNotifications();
+ TestableLooper.get(this).processAllMessages();
+
try {
mService.onDestroy();
} catch (IllegalStateException | IllegalArgumentException e) {
+ Log.e(TAG, "failed to destroy", e);
// can throw if a broadcast receiver was never registered
}
@@ -846,6 +913,11 @@
// could cause issues, for example, messages that remove/cancel shown toasts (this causes
// problematic interactions with mocks when they're no longer working as expected).
mWorkerHandler.removeCallbacksAndMessages(null);
+
+ if (TestableLooper.get(this) != null) {
+ // Must remove static reference to this test object to prevent leak (b/261039202)
+ TestableLooper.remove(this);
+ }
}
private void simulatePackageSuspendBroadcast(boolean suspend, String pkg,
@@ -1005,7 +1077,9 @@
Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
- .addAction(new Notification.Action.Builder(null, "test", null).build());
+ .addAction(new Notification.Action.Builder(null, "test", mActivityIntent).build())
+ .addAction(new Notification.Action.Builder(
+ null, "test", mActivityIntentImmutable).build());
if (extender != null) {
nb.extend(extender);
}
@@ -1045,18 +1119,20 @@
private NotificationRecord generateMessageBubbleNotifRecord(NotificationChannel channel,
String tag) {
- return generateMessageBubbleNotifRecord(true, channel, 1, tag, null, false);
+ return generateMessageBubbleNotifRecord(true, channel, 1, tag, null, false, true);
}
private NotificationRecord generateMessageBubbleNotifRecord(boolean addMetadata,
- NotificationChannel channel, int id, String tag, String groupKey, boolean isSummary) {
+ NotificationChannel channel, int id, String tag, String groupKey, boolean isSummary,
+ boolean mutable) {
if (channel == null) {
channel = mTestNotificationChannel;
}
if (tag == null) {
tag = "tag";
}
- Notification.Builder nb = getMessageStyleNotifBuilder(addMetadata, groupKey, isSummary);
+ Notification.Builder nb = getMessageStyleNotifBuilder(addMetadata, groupKey,
+ isSummary, mutable);
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id,
tag, mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
@@ -1129,18 +1205,15 @@
}
private Notification.Builder getMessageStyleNotifBuilder(boolean addBubbleMetadata,
- String groupKey, boolean isSummary) {
+ String groupKey, boolean isSummary, boolean mutable) {
// Give it a person
Person person = new Person.Builder()
.setName("bubblebot")
.build();
RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
- PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0,
- new Intent().setPackage(mContext.getPackageName()),
- PendingIntent.FLAG_MUTABLE);
Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
- inputIntent).addRemoteInput(remoteInput)
+ mutable ? mActivityIntent : mActivityIntentImmutable).addRemoteInput(remoteInput)
.build();
// Make it messaging style
Notification.Builder nb = new Notification.Builder(mContext,
@@ -1167,17 +1240,14 @@
}
private Notification.BubbleMetadata getBubbleMetadata() {
- PendingIntent pendingIntent = mock(PendingIntent.class);
- Intent intent = mock(Intent.class);
- when(pendingIntent.getIntent()).thenReturn(intent);
- when(pendingIntent.getTarget()).thenReturn(pi1);
-
ActivityInfo info = new ActivityInfo();
info.resizeMode = RESIZE_MODE_RESIZEABLE;
- when(intent.resolveActivityInfo(any(), anyInt())).thenReturn(info);
+ ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = info;
+ when(mPackageManagerClient.resolveActivity(any(), anyInt())).thenReturn(ri);
return new Notification.BubbleMetadata.Builder(
- pendingIntent,
+ mActivityIntent,
Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon))
.build();
}
@@ -1189,7 +1259,8 @@
// Notification that has bubble metadata
NotificationRecord nrBubble = generateMessageBubbleNotifRecord(true /* addMetadata */,
- mTestNotificationChannel, 1 /* id */, "tag", groupKey, false /* isSummary */);
+ mTestNotificationChannel, 1 /* id */, "tag", groupKey, false /* isSummary */,
+ true);
mBinderService.enqueueNotificationWithTag(mPkg, mPkg, nrBubble.getSbn().getTag(),
nrBubble.getSbn().getId(), nrBubble.getSbn().getNotification(),
@@ -1203,7 +1274,8 @@
// Notification without bubble metadata
NotificationRecord nrPlain = generateMessageBubbleNotifRecord(false /* addMetadata */,
- mTestNotificationChannel, 2 /* id */, "tag", groupKey, false /* isSummary */);
+ mTestNotificationChannel, 2 /* id */, "tag", groupKey, false /* isSummary */,
+ true);
mBinderService.enqueueNotificationWithTag(mPkg, mPkg, nrPlain.getSbn().getTag(),
nrPlain.getSbn().getId(), nrPlain.getSbn().getNotification(),
@@ -1215,7 +1287,8 @@
// Summary notification for both of those
NotificationRecord nrSummary = generateMessageBubbleNotifRecord(false /* addMetadata */,
- mTestNotificationChannel, 3 /* id */, "tag", groupKey, true /* isSummary */);
+ mTestNotificationChannel, 3 /* id */, "tag", groupKey, true /* isSummary */,
+ true);
if (summaryAutoCancel) {
nrSummary.getNotification().flags |= FLAG_AUTO_CANCEL;
@@ -1232,6 +1305,7 @@
}
@Test
+ @DisableFlags(FLAG_ALL_NOTIFS_NEED_TTL)
public void testLimitTimeOutBroadcast() {
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_HIGH);
@@ -2501,8 +2575,8 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testCancelWithTagDoesNotCancelLifetimeExtended() throws Exception {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
final NotificationRecord notif = generateNotificationRecord(null);
notif.getSbn().getNotification().flags =
Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
@@ -2529,19 +2603,11 @@
assertThat(captor.getValue().getNotification().flags
& FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(
FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
-
- mSetFlagsRule.disableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
- mBinderService.cancelNotificationWithTag(mPkg, mPkg, sbn.getTag(), sbn.getId(),
- sbn.getUserId());
- waitForIdle();
-
- assertThat(mBinderService.getActiveNotifications(sbn.getPackageName()).length).isEqualTo(0);
- assertThat(mService.getNotificationRecordCount()).isEqualTo(0);
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testCancelAllDoesNotCancelLifetimeExtended() throws Exception {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
// Adds a lifetime extended notification.
final NotificationRecord notif = generateNotificationRecord(mTestNotificationChannel, 1,
null, false);
@@ -2978,9 +3044,9 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testCancelNotificationsFromListener_clearAll_NoClearLifetimeExt()
throws Exception {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
final NotificationRecord notif = generateNotificationRecord(
mTestNotificationChannel, 1, null, false);
notif.getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
@@ -3211,9 +3277,9 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testCancelNotificationsFromListener_byKey_NoClearLifetimeExt()
throws Exception {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
final NotificationRecord notif = generateNotificationRecord(
mTestNotificationChannel, 3, null, false);
notif.getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
@@ -5695,12 +5761,16 @@
verify(mZenModeHelper).updateZenRulesOnLocaleChange();
}
- private void simulateNotificationTimeoutBroadcast(String notificationKey) {
- final Bundle extras = new Bundle();
- extras.putString(EXTRA_KEY, notificationKey);
- final Intent intent = new Intent(ACTION_NOTIFICATION_TIMEOUT);
- intent.putExtras(extras);
- mNotificationTimeoutReceiver.onReceive(getContext(), intent);
+ private void simulateNotificationTimeout(String notificationKey) {
+ if (Flags.allNotifsNeedTtl()) {
+ mService.mNotificationManagerPrivate.timeoutNotification(notificationKey);
+ } else {
+ final Bundle extras = new Bundle();
+ extras.putString(EXTRA_KEY, notificationKey);
+ final Intent intent = new Intent(ACTION_NOTIFICATION_TIMEOUT);
+ intent.putExtras(extras);
+ mNotificationTimeoutReceiver.onReceive(getContext(), intent);
+ }
}
@Test
@@ -5709,7 +5779,7 @@
mTestNotificationChannel, 1, null, false);
mService.addNotification(notif);
- simulateNotificationTimeoutBroadcast(notif.getKey());
+ simulateNotificationTimeout(notif.getKey());
waitForIdle();
// Check that the notification was cancelled.
@@ -5725,7 +5795,7 @@
notif.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
mService.addNotification(notif);
- simulateNotificationTimeoutBroadcast(notif.getKey());
+ simulateNotificationTimeout(notif.getKey());
waitForIdle();
// Check that the notification was not cancelled.
@@ -5741,7 +5811,7 @@
notif.getSbn().getNotification().flags = Notification.FLAG_USER_INITIATED_JOB;
mService.addNotification(notif);
- simulateNotificationTimeoutBroadcast(notif.getKey());
+ simulateNotificationTimeout(notif.getKey());
waitForIdle();
// Check that the notification was not cancelled.
@@ -5751,15 +5821,15 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testTimeout_NoCancelLifetimeExtensionNotification() throws Exception {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
// Create a notification with FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY
final NotificationRecord notif = generateNotificationRecord(null);
notif.getSbn().getNotification().flags =
Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
mService.addNotification(notif);
- simulateNotificationTimeoutBroadcast(notif.getKey());
+ simulateNotificationTimeout(notif.getKey());
waitForIdle();
// Check that the notification was not cancelled.
@@ -5839,8 +5909,8 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testStats_DirectReplyLifetimeExtendedPostsUpdate() throws Exception {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
mService.addNotification(r);
@@ -6662,6 +6732,7 @@
verify(visitor, times(1)).accept(eq(personIcon.getUri()));
verify(visitor, times(1)).accept(eq(verificationIcon.getUri()));
verify(visitor, times(1)).accept(eq(hangUpUri));
+ hangUpIntent.cancel();
}
@Test
@@ -6691,6 +6762,8 @@
verify(visitor, times(1)).accept(eq(verificationIcon.getUri()));
verify(visitor, times(1)).accept(eq(answerIntent.getIntent().getData()));
verify(visitor, times(1)).accept(eq(declineUri));
+ answerIntent.cancel();
+ declineIntent.cancel();
}
@Test
@@ -6767,6 +6840,9 @@
verify(visitor, times(1)).accept(eq(actionIntentUri));
verify(visitor).accept(eq(wearActionIcon.getUri()));
verify(visitor, times(1)).accept(eq(wearActionIntentUri));
+ displayIntent.cancel();
+ actionIntent.cancel();
+ wearActionIntent.cancel();
}
@Test
@@ -6788,6 +6864,8 @@
verify(visitor, times(1)).accept(eq(contentIntentUri));
verify(visitor, times(1)).accept(eq(deleteIntentUri));
+ contentIntent.cancel();
+ deleteIntent.cancel();
}
@Test
@@ -6813,6 +6891,8 @@
verify(visitor, times(1)).accept(eq(readPendingIntentUri));
verify(visitor, times(1)).accept(eq(replyPendingIntentUri));
+ readPendingIntent.cancel();
+ replyPendingIntent.cancel();
}
@Test
@@ -7916,8 +7996,9 @@
setAppInForegroundForToasts(mUid, true);
// enqueue toast -> toast should still enqueue
- enqueueToast(testPackage, new TestableToastCallback());
+ boolean wasEnqueued = enqueueToast(testPackage, new TestableToastCallback());
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
}
@Test
@@ -7936,8 +8017,9 @@
setAppInForegroundForToasts(mUid, false);
// enqueue toast -> no toasts enqueued
- enqueueToast(testPackage, new TestableToastCallback());
+ boolean wasEnqueued = enqueueToast(testPackage, new TestableToastCallback());
assertEquals(0, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isFalse();
}
@Test
@@ -8045,8 +8127,9 @@
setAppInForegroundForToasts(mUid, true);
// enqueue toast -> toast should still enqueue
- enqueueTextToast(testPackage, "Text");
+ boolean wasEnqueued = enqueueTextToast(testPackage, "Text");
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
}
@Test
@@ -8065,8 +8148,9 @@
setAppInForegroundForToasts(mUid, false);
// enqueue toast -> toast should still enqueue
- enqueueTextToast(testPackage, "Text");
+ boolean wasEnqueued = enqueueTextToast(testPackage, "Text");
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
}
@Test
@@ -8220,8 +8304,9 @@
setAppInForegroundForToasts(mUid, false);
// enqueue toast -> toast should still enqueue
- enqueueToast(testPackage, new TestableToastCallback());
+ boolean wasEnqueued = enqueueToast(testPackage, new TestableToastCallback());
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
verify(mAm).setProcessImportant(any(), anyInt(), eq(true), any());
}
@@ -8242,8 +8327,9 @@
setAppInForegroundForToasts(mUid, true);
// enqueue toast -> toast should still enqueue
- enqueueTextToast(testPackage, "Text");
+ boolean wasEnqueued = enqueueTextToast(testPackage, "Text");
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
verify(mAm).setProcessImportant(any(), anyInt(), eq(false), any());
}
@@ -8264,8 +8350,9 @@
setAppInForegroundForToasts(mUid, false);
// enqueue toast -> toast should still enqueue
- enqueueTextToast(testPackage, "Text");
+ boolean wasEnqueued = enqueueTextToast(testPackage, "Text");
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
verify(mAm).setProcessImportant(any(), anyInt(), eq(false), any());
}
@@ -8274,7 +8361,8 @@
allowTestPackageToToast();
// enqueue toast -> no toasts enqueued
- enqueueTextToast(TEST_PACKAGE, "Text");
+ boolean wasEnqueued = enqueueTextToast(TEST_PACKAGE, "Text");
+ assertThat(wasEnqueued).isTrue();
verifyToastShownForTestPackage("Text", DEFAULT_DISPLAY);
}
@@ -8367,10 +8455,11 @@
.thenReturn(false);
// enqueue toast -> no toasts enqueued
- enqueueTextToast(testPackage, "Text");
+ boolean wasEnqueued = enqueueTextToast(testPackage, "Text");
verify(mStatusBar, never()).showToast(anyInt(), any(), any(), any(), any(), anyInt(), any(),
anyInt());
assertEquals(0, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isFalse();
}
@Test
@@ -8390,10 +8479,11 @@
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
// enqueue toast -> no toasts enqueued
- enqueueToast(testPackage, new TestableToastCallback());
+ boolean wasEnqueued = enqueueToast(testPackage, new TestableToastCallback());
verify(mStatusBar, never()).showToast(anyInt(), any(), any(), any(), any(), anyInt(), any(),
anyInt());
assertEquals(0, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isFalse();
}
@Test
@@ -8415,8 +8505,9 @@
setAppInForegroundForToasts(mUid, false);
// enqueue toast -> no toasts enqueued
- enqueueToast(testPackage, new TestableToastCallback());
+ boolean wasEnqueued = enqueueToast(testPackage, new TestableToastCallback());
assertEquals(0, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isFalse();
}
@Test
@@ -8437,8 +8528,9 @@
setAppInForegroundForToasts(mUid, false);
// enqueue toast -> system toast can still be enqueued
- enqueueToast(testPackage, new TestableToastCallback());
+ boolean wasEnqueued = enqueueToast(testPackage, new TestableToastCallback());
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
}
@Test
@@ -8458,7 +8550,12 @@
// Trying to quickly enqueue more toast than allowed.
for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_TOASTS + 1; i++) {
- enqueueTextToast(testPackage, "Text");
+ boolean wasEnqueued = enqueueTextToast(testPackage, "Text");
+ if (i < NotificationManagerService.MAX_PACKAGE_TOASTS) {
+ assertThat(wasEnqueued).isTrue();
+ } else {
+ assertThat(wasEnqueued).isFalse();
+ }
}
// Only allowed number enqueued, rest ignored.
assertEquals(NotificationManagerService.MAX_PACKAGE_TOASTS, mService.mToastQueue.size());
@@ -8570,8 +8667,8 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testOnNotificationSmartReplySent() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
final int replyIndex = 2;
final String reply = "Hello";
final boolean modifiedBeforeSending = true;
@@ -8596,8 +8693,8 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testStats_SmartReplyAlreadyLifetimeExtendedPostsUpdate() throws Exception {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
final int replyIndex = 2;
final String reply = "Hello";
final boolean modifiedBeforeSending = true;
@@ -8630,8 +8727,7 @@
public void testOnNotificationActionClick() {
final int actionIndex = 2;
final Notification.Action action =
- new Notification.Action.Builder(null, "text", PendingIntent.getActivity(
- mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)).build();
+ new Notification.Action.Builder(null, "text", mActivityIntent).build();
final boolean generatedByAssistant = false;
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
@@ -8652,9 +8748,8 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testActionClickLifetimeExtendedCancel() throws Exception {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
-
final Notification.Action action =
new Notification.Action.Builder(null, "text", PendingIntent.getActivity(
mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)).build();
@@ -8780,8 +8875,7 @@
public void testOnAssistantNotificationActionClick() {
final int actionIndex = 1;
final Notification.Action action =
- new Notification.Action.Builder(null, "text", PendingIntent.getActivity(
- mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)).build();
+ new Notification.Action.Builder(null, "text", mActivityIntent).build();
final boolean generatedByAssistant = true;
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
@@ -9296,7 +9390,7 @@
BUBBLE_PREFERENCE_ALL /* app */,
true /* channel */);
- Notification.Builder nb = getMessageStyleNotifBuilder(true, null, false);
+ Notification.Builder nb = getMessageStyleNotifBuilder(true, null, false, true);
nb.setShortcutId(null);
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1,
null, mUid, 0,
@@ -9340,7 +9434,7 @@
// Messaging notif WITHOUT bubble metadata
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addBubbleMetadata */,
- null /* groupKey */, false /* isSummary */);
+ null /* groupKey */, false /* isSummary */, true);
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1,
"testFlagBubbleNotifs_noFlag_notBubble", mUid, 0,
@@ -10598,7 +10692,7 @@
Notification.BubbleMetadata metadata =
new Notification.BubbleMetadata.Builder(VALID_CONVO_SHORTCUT_ID).build();
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
- null /* groupKey */, false /* isSummary */);
+ null /* groupKey */, false /* isSummary */, true);
nb.setShortcutId(VALID_CONVO_SHORTCUT_ID);
nb.setBubbleMetadata(metadata);
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1,
@@ -10658,7 +10752,7 @@
Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
shortcutId).build();
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
- null /* groupKey */, false /* isSummary */);
+ null /* groupKey */, false /* isSummary */, true);
nb.setShortcutId(shortcutId);
nb.setBubbleMetadata(metadata);
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1,
@@ -11051,7 +11145,7 @@
//Create notification record
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
- null /* groupKey */, false /* isSummary */);
+ null /* groupKey */, false /* isSummary */, true);
nb.setShortcutId(VALID_CONVO_SHORTCUT_ID);
nb.setChannelId(originalChannel.getId());
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1,
@@ -11087,7 +11181,7 @@
//Create notification record
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
- null /* groupKey */, false /* isSummary */);
+ null /* groupKey */, false /* isSummary */, true);
nb.setShortcutId(VALID_CONVO_SHORTCUT_ID);
nb.setChannelId(originalChannel.getId());
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1,
@@ -11131,7 +11225,7 @@
//Create notification record
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
- null /* groupKey */, false /* isSummary */);
+ null /* groupKey */, false /* isSummary */, true);
nb.setShortcutId(VALID_CONVO_SHORTCUT_ID);
nb.setChannelId(originalChannel.getId());
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1,
@@ -11176,7 +11270,7 @@
//Create notification record without a shortcutId
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
- null /* groupKey */, false /* isSummary */);
+ null /* groupKey */, false /* isSummary */, true);
nb.setShortcutId(null);
nb.setChannelId(originalChannel.getId());
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1,
@@ -11311,7 +11405,7 @@
@Test
public void testRecordMessages_invalidMsg() throws RemoteException {
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
- null /* groupKey */, false /* isSummary */);
+ null /* groupKey */, false /* isSummary */, true);
nb.setShortcutId(null);
StatusBarNotification sbn = new StatusBarNotification(PKG_P, PKG_P, 1,
"testRecordMessages_invalidMsg", mUid, 0, nb.build(),
@@ -11352,7 +11446,7 @@
@Test
public void testRecordMessages_validMsg() throws RemoteException {
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
- null /* groupKey */, false /* isSummary */);
+ null /* groupKey */, false /* isSummary */, true);
nb.setShortcutId(null);
StatusBarNotification sbn = new StatusBarNotification(PKG_P, PKG_P, 1,
"testRecordMessages_validMsg", mUid, 0, nb.build(),
@@ -11388,7 +11482,7 @@
waitForIdle();
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
- null /* groupKey */, false /* isSummary */);
+ null /* groupKey */, false /* isSummary */, true);
nb.setShortcutId(null);
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1,
"testRecordMessages_invalidMsg_afterValidMsg_2", mUid, 0, nb.build(),
@@ -11608,10 +11702,9 @@
@Test
public void testImmutableBubbleIntent() throws Exception {
- when(mAmi.getPendingIntentFlags(pi1))
- .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+ when(mAmi.getPendingIntentFlags(any())).thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
NotificationRecord r = generateMessageBubbleNotifRecord(true,
- mTestNotificationChannel, 7, "testImmutableBubbleIntent", null, false);
+ mTestNotificationChannel, 7, "testImmutableBubbleIntent", null, false, false);
try {
mBinderService.enqueueNotificationWithTag(mPkg, mPkg, r.getSbn().getTag(),
r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
@@ -11625,10 +11718,8 @@
@Test
public void testMutableBubbleIntent() throws Exception {
- when(mAmi.getPendingIntentFlags(pi1))
- .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
NotificationRecord r = generateMessageBubbleNotifRecord(true,
- mTestNotificationChannel, 7, "testMutableBubbleIntent", null, false);
+ mTestNotificationChannel, 7, "testMutableBubbleIntent", null, false, true);
mBinderService.enqueueNotificationWithTag(mPkg, mPkg, r.getSbn().getTag(),
r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
@@ -11641,10 +11732,10 @@
@Test
public void testImmutableDirectReplyActionIntent() throws Exception {
- when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
- .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+ when(mAmi.getPendingIntentFlags(any())).thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
NotificationRecord r = generateMessageBubbleNotifRecord(false,
- mTestNotificationChannel, 7, "testImmutableDirectReplyActionIntent", null, false);
+ mTestNotificationChannel, 7, "testImmutableDirectReplyActionIntent", null, false,
+ false);
try {
mBinderService.enqueueNotificationWithTag(mPkg, mPkg, r.getSbn().getTag(),
r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
@@ -11658,10 +11749,9 @@
@Test
public void testMutableDirectReplyActionIntent() throws Exception {
- when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
- .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
NotificationRecord r = generateMessageBubbleNotifRecord(false,
- mTestNotificationChannel, 7, "testMutableDirectReplyActionIntent", null, false);
+ mTestNotificationChannel, 7, "testMutableDirectReplyActionIntent", null, false,
+ true);
mBinderService.enqueueNotificationWithTag(mPkg, mPkg, r.getSbn().getTag(),
r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
@@ -11673,18 +11763,15 @@
@Test
public void testImmutableDirectReplyContextualActionIntent() throws Exception {
- when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
- .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+ when(mAmi.getPendingIntentFlags(any())).thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
ArrayList<Notification.Action> extraAction = new ArrayList<>();
RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
- PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(),
- PendingIntent.FLAG_IMMUTABLE);
Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
- inputIntent).addRemoteInput(remoteInput)
+ mActivityIntentImmutable).addRemoteInput(remoteInput)
.build();
extraAction.add(replyAction);
Bundle signals = new Bundle();
@@ -11705,18 +11792,13 @@
@Test
public void testMutableDirectReplyContextualActionIntent() throws Exception {
- when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
- .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
ArrayList<Notification.Action> extraAction = new ArrayList<>();
RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
- PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0,
- new Intent().setPackage(mContext.getPackageName()),
- PendingIntent.FLAG_MUTABLE);
Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
- inputIntent).addRemoteInput(remoteInput)
+ mActivityIntent).addRemoteInput(remoteInput)
.build();
extraAction.add(replyAction);
Bundle signals = new Bundle();
@@ -11732,10 +11814,8 @@
@Test
public void testImmutableActionIntent() throws Exception {
- when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
- .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+ when(mAmi.getPendingIntentFlags(any())).thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
-
mBinderService.enqueueNotificationWithTag(mPkg, mPkg, r.getSbn().getTag(),
r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
@@ -11747,12 +11827,11 @@
@Test
public void testImmutableContextualActionIntent() throws Exception {
- when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
- .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+ when(mAmi.getPendingIntentFlags(any())).thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
ArrayList<Notification.Action> extraAction = new ArrayList<>();
- extraAction.add(new Notification.Action(0, "hello", null));
+ extraAction.add(new Notification.Action(0, "hello", mActivityIntentImmutable));
Bundle signals = new Bundle();
signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
@@ -12104,8 +12183,6 @@
@Test
public void testCallNotificationsBypassBlock() throws Exception {
- when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
- .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
Notification.Builder nb = new Notification.Builder(
@@ -12128,8 +12205,8 @@
.setName("caller")
.build();
nb.setStyle(Notification.CallStyle.forOngoingCall(
- person, mock(PendingIntent.class)));
- nb.setFullScreenIntent(mock(PendingIntent.class), true);
+ person, mActivityIntent));
+ nb.setFullScreenIntent(mActivityIntent, true);
sbn = new StatusBarNotification(mPkg, mPkg, 8, "tag", mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
@@ -12197,8 +12274,8 @@
mService.clearNotifications();
reset(mUsageStats);
Person person = new Person.Builder().setName("caller").build();
- nb.setStyle(Notification.CallStyle.forOngoingCall(person, mock(PendingIntent.class)));
- nb.setFullScreenIntent(mock(PendingIntent.class), true);
+ nb.setStyle(Notification.CallStyle.forOngoingCall(person, mActivityIntent));
+ nb.setFullScreenIntent(mActivityIntent, true);
sbn = new StatusBarNotification(mPkg, mPkg, 8, "tag", mUid, 0, nb.build(),
UserHandle.getUserHandleForUid(mUid), null, 0);
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
@@ -12692,7 +12769,7 @@
Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
NotificationManagerService.REVIEW_NOTIF_STATE_SHOULD_SHOW);
mService.maybeShowInitialReviewPermissionsNotification();
- verify(mMockNm, times(1)).notify(eq(NotificationManagerService.TAG),
+ verify(mMockNm, times(1)).notify(eq(TAG),
eq(SystemMessageProto.SystemMessage.NOTE_REVIEW_NOTIFICATION_PERMISSIONS),
any(Notification.class));
}
@@ -12727,7 +12804,7 @@
Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
NotificationManagerService.REVIEW_NOTIF_STATE_RESHOWN);
mService.maybeShowInitialReviewPermissionsNotification();
- verify(mMockNm, times(1)).notify(eq(NotificationManagerService.TAG),
+ verify(mMockNm, times(1)).notify(eq(TAG),
eq(SystemMessageProto.SystemMessage.NOTE_REVIEW_NOTIFICATION_PERMISSIONS),
any(Notification.class));
}
@@ -12743,7 +12820,7 @@
mInternalService.sendReviewPermissionsNotification();
// Notification should be sent
- verify(mMockNm, times(1)).notify(eq(NotificationManagerService.TAG),
+ verify(mMockNm, times(1)).notify(eq(TAG),
eq(SystemMessageProto.SystemMessage.NOTE_REVIEW_NOTIFICATION_PERMISSIONS),
any(Notification.class));
@@ -12775,7 +12852,7 @@
.thenReturn(permissionState);
Notification n = new Notification.Builder(mContext, "test")
- .setFullScreenIntent(mock(PendingIntent.class), true)
+ .setFullScreenIntent(mActivityIntent, true)
.build();
mService.fixNotification(n, mPkg, "tag", 9, mUserId, mUid, NOT_FOREGROUND_SERVICE, true);
@@ -12843,7 +12920,7 @@
Person person = new Person.Builder().setName("caller").build();
Notification n = new Notification.Builder(mContext, "test")
.setStyle(Notification.CallStyle.forOngoingCall(
- person, mock(PendingIntent.class)))
+ person, mActivityIntent))
.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, "tag", mUid, 0,
n, UserHandle.getUserHandleForUid(mUid), null, 0);
@@ -12864,7 +12941,7 @@
Notification n = new Notification.Builder(mContext, "test")
.setFlag(FLAG_FOREGROUND_SERVICE, true)
.setStyle(Notification.CallStyle.forOngoingCall(
- person, mock(PendingIntent.class)))
+ person, mActivityIntent))
.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, "tag", mUid, 0,
n, UserHandle.getUserHandleForUid(mUid), null, 0);
@@ -13023,8 +13100,7 @@
Notification n = new Notification.Builder(mContext, "test")
// Without FLAG_FOREGROUND_SERVICE.
//.setFlag(FLAG_FOREGROUND_SERVICE, true)
- .setStyle(Notification.CallStyle.forOngoingCall(
- person, mock(PendingIntent.class)))
+ .setStyle(Notification.CallStyle.forOngoingCall(person, mActivityIntent))
.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, "tag", mUid, 0,
n, UserHandle.getUserHandleForUid(mUid), null, 0);
@@ -13040,8 +13116,7 @@
Person person = new Person.Builder().setName("caller").build();
Notification n = new Notification.Builder(mContext, "test")
.setFlag(FLAG_USER_INITIATED_JOB, true)
- .setStyle(Notification.CallStyle.forOngoingCall(
- person, mock(PendingIntent.class)))
+ .setStyle(Notification.CallStyle.forOngoingCall(person, mActivityIntent))
.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, "tag", mUid, 0,
n, UserHandle.getUserHandleForUid(mUid), null, 0);
@@ -13055,9 +13130,8 @@
public void checkCallStyleNotification_allowedForFsiAllowed() throws Exception {
Person person = new Person.Builder().setName("caller").build();
Notification n = new Notification.Builder(mContext, "test")
- .setFullScreenIntent(mock(PendingIntent.class), true)
- .setStyle(Notification.CallStyle.forOngoingCall(
- person, mock(PendingIntent.class)))
+ .setFullScreenIntent(mActivityIntent, true)
+ .setStyle(Notification.CallStyle.forOngoingCall(person, mActivityIntent))
.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, "tag", mUid, 0,
n, UserHandle.getUserHandleForUid(mUid), null, 0);
@@ -13072,8 +13146,7 @@
Person person = new Person.Builder().setName("caller").build();
Notification n = new Notification.Builder(mContext, "test")
.setFlag(Notification.FLAG_FSI_REQUESTED_BUT_DENIED, true)
- .setStyle(Notification.CallStyle.forOngoingCall(
- person, mock(PendingIntent.class)))
+ .setStyle(Notification.CallStyle.forOngoingCall(person, mActivityIntent))
.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, "tag", mUid, 0,
n, UserHandle.getUserHandleForUid(mUid), null, 0);
@@ -13153,6 +13226,33 @@
}
@Test
+ public void fixSystemNotification_defaultAdservices_withOnGoingFlag_nondismissible()
+ throws Exception {
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = ADSERVICES_APK_PKG;
+ ai.uid = mUid;
+ ai.flags |= ApplicationInfo.FLAG_SYSTEM;
+
+ when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(ai);
+ when(mAppOpsManager.checkOpNoThrow(
+ AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, ai.uid,
+ ai.packageName)).thenReturn(AppOpsManager.MODE_IGNORED);
+ // Given: a notification from an app on the system partition has the flag
+ // FLAG_ONGOING_EVENT set
+ Notification n = new Notification.Builder(mContext, "test")
+ .setOngoing(true)
+ .build();
+
+ // When: fix the notification with NotificationManagerService
+ mService.fixNotification(n, ADSERVICES_APK_PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE,
+ true);
+
+ // Then: the notification's flag FLAG_NO_DISMISS should be set
+ assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
+ }
+
+ @Test
public void fixCallNotification_withOnGoingFlag_shouldNotBeNonDismissible()
throws Exception {
// Given: a call notification has the flag FLAG_ONGOING_EVENT set
@@ -13161,8 +13261,7 @@
.build();
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(true)
- .setStyle(Notification.CallStyle.forOngoingCall(
- person, mock(PendingIntent.class)))
+ .setStyle(Notification.CallStyle.forOngoingCall(person, mActivityIntent))
.build();
// When: fix the notification with NotificationManagerService
@@ -13994,6 +14093,314 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
+ public void enqueueNotification_acceptsCorrectToken() throws RemoteException {
+ Notification sent = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentIntent(createPendingIntent("content"))
+ .build();
+ Notification received = parcelAndUnparcel(sent, Notification.CREATOR);
+ assertThat(received.getAllowlistToken()).isEqualTo(
+ NotificationManagerService.ALLOWLIST_TOKEN);
+
+ mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", 1,
+ parcelAndUnparcel(received, Notification.CREATOR), mUserId);
+ waitForIdle();
+
+ assertThat(mService.mNotificationList).hasSize(1);
+ assertThat(mService.mNotificationList.get(0).getNotification().getAllowlistToken())
+ .isEqualTo(NotificationManagerService.ALLOWLIST_TOKEN);
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
+ public void enqueueNotification_acceptsNullToken_andPopulatesIt() throws RemoteException {
+ Notification receivedWithoutParceling = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentIntent(createPendingIntent("content"))
+ .build();
+ assertThat(receivedWithoutParceling.getAllowlistToken()).isNull();
+
+ mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", 1,
+ parcelAndUnparcel(receivedWithoutParceling, Notification.CREATOR), mUserId);
+ waitForIdle();
+
+ assertThat(mService.mNotificationList).hasSize(1);
+ assertThat(mService.mNotificationList.get(0).getNotification().getAllowlistToken())
+ .isEqualTo(NotificationManagerService.ALLOWLIST_TOKEN);
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
+ public void enqueueNotification_rejectsOtherToken() throws RemoteException {
+ Notification sent = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentIntent(createPendingIntent("content"))
+ .build();
+ sent.overrideAllowlistToken(new Binder());
+ Notification received = parcelAndUnparcel(sent, Notification.CREATOR);
+ assertThat(received.getAllowlistToken()).isEqualTo(sent.getAllowlistToken());
+
+ assertThrows(SecurityException.class, () ->
+ mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", 1,
+ parcelAndUnparcel(received, Notification.CREATOR), mUserId));
+ waitForIdle();
+
+ assertThat(mService.mNotificationList).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
+ public void enqueueNotification_customParcelingWithFakeInnerToken_hasCorrectTokenInIntents()
+ throws RemoteException {
+ Notification sentFromApp = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentIntent(createPendingIntent("content"))
+ .setPublicVersion(new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentIntent(createPendingIntent("public"))
+ .build())
+ .build();
+ sentFromApp.publicVersion.overrideAllowlistToken(new Binder());
+
+ // Instead of using the normal parceling, assume the caller parcels it by hand, including a
+ // null token in the outer notification (as would be expected, and as is verified by
+ // enqueue) but trying to sneak in a different one in the inner notification, hoping it gets
+ // propagated to the PendingIntents.
+ Parcel parcelSentFromApp = Parcel.obtain();
+ writeNotificationToParcelCustom(parcelSentFromApp, sentFromApp, new ArraySet<>(
+ Lists.newArrayList(sentFromApp.contentIntent,
+ sentFromApp.publicVersion.contentIntent)));
+
+ // Use the unparceling as received in enqueueNotificationWithTag()
+ parcelSentFromApp.setDataPosition(0);
+ Notification receivedByNms = new Notification(parcelSentFromApp);
+
+ // Verify that all the pendingIntents have the correct token.
+ assertThat(receivedByNms.contentIntent.getWhitelistToken()).isEqualTo(
+ NotificationManagerService.ALLOWLIST_TOKEN);
+ assertThat(receivedByNms.publicVersion.contentIntent.getWhitelistToken()).isEqualTo(
+ NotificationManagerService.ALLOWLIST_TOKEN);
+ }
+
+ /**
+ * Replicates the behavior of {@link Notification#writeToParcel} but excluding the
+ * "always use the same allowlist token as the root notification" parts.
+ */
+ private static void writeNotificationToParcelCustom(Parcel parcel, Notification notif,
+ ArraySet<PendingIntent> allPendingIntents) {
+ int flags = 0;
+ parcel.writeInt(1); // version?
+
+ parcel.writeStrongBinder(notif.getAllowlistToken());
+ parcel.writeLong(notif.when);
+ parcel.writeLong(notif.creationTime);
+ if (notif.getSmallIcon() != null) {
+ parcel.writeInt(1);
+ notif.getSmallIcon().writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+ parcel.writeInt(notif.number);
+ if (notif.contentIntent != null) {
+ parcel.writeInt(1);
+ notif.contentIntent.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (notif.deleteIntent != null) {
+ parcel.writeInt(1);
+ notif.deleteIntent.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (notif.tickerText != null) {
+ parcel.writeInt(1);
+ TextUtils.writeToParcel(notif.tickerText, parcel, flags);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (notif.tickerView != null) {
+ parcel.writeInt(1);
+ notif.tickerView.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (notif.contentView != null) {
+ parcel.writeInt(1);
+ notif.contentView.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+ if (notif.getLargeIcon() != null) {
+ parcel.writeInt(1);
+ notif.getLargeIcon().writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+
+ parcel.writeInt(notif.defaults);
+ parcel.writeInt(notif.flags);
+
+ if (notif.sound != null) {
+ parcel.writeInt(1);
+ notif.sound.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+ parcel.writeInt(notif.audioStreamType);
+
+ if (notif.audioAttributes != null) {
+ parcel.writeInt(1);
+ notif.audioAttributes.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+
+ parcel.writeLongArray(notif.vibrate);
+ parcel.writeInt(notif.ledARGB);
+ parcel.writeInt(notif.ledOnMS);
+ parcel.writeInt(notif.ledOffMS);
+ parcel.writeInt(notif.iconLevel);
+
+ if (notif.fullScreenIntent != null) {
+ parcel.writeInt(1);
+ notif.fullScreenIntent.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+
+ parcel.writeInt(notif.priority);
+
+ parcel.writeString8(notif.category);
+
+ parcel.writeString8(notif.getGroup());
+
+ parcel.writeString8(notif.getSortKey());
+
+ parcel.writeBundle(notif.extras); // null ok
+
+ parcel.writeTypedArray(notif.actions, 0); // null ok
+
+ if (notif.bigContentView != null) {
+ parcel.writeInt(1);
+ notif.bigContentView.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+
+ if (notif.headsUpContentView != null) {
+ parcel.writeInt(1);
+ notif.headsUpContentView.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+
+ parcel.writeInt(notif.visibility);
+
+ if (notif.publicVersion != null) {
+ parcel.writeInt(1);
+ writeNotificationToParcelCustom(parcel, notif.publicVersion, new ArraySet<>());
+ } else {
+ parcel.writeInt(0);
+ }
+
+ parcel.writeInt(notif.color);
+
+ if (notif.getChannelId() != null) {
+ parcel.writeInt(1);
+ parcel.writeString8(notif.getChannelId());
+ } else {
+ parcel.writeInt(0);
+ }
+ parcel.writeLong(notif.getTimeoutAfter());
+
+ if (notif.getShortcutId() != null) {
+ parcel.writeInt(1);
+ parcel.writeString8(notif.getShortcutId());
+ } else {
+ parcel.writeInt(0);
+ }
+
+ if (notif.getLocusId() != null) {
+ parcel.writeInt(1);
+ notif.getLocusId().writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+
+ parcel.writeInt(notif.getBadgeIconType());
+
+ if (notif.getSettingsText() != null) {
+ parcel.writeInt(1);
+ TextUtils.writeToParcel(notif.getSettingsText(), parcel, flags);
+ } else {
+ parcel.writeInt(0);
+ }
+
+ parcel.writeInt(notif.getGroupAlertBehavior());
+
+ if (notif.getBubbleMetadata() != null) {
+ parcel.writeInt(1);
+ notif.getBubbleMetadata().writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+
+ parcel.writeBoolean(notif.getAllowSystemGeneratedContextualActions());
+
+ parcel.writeInt(Notification.FOREGROUND_SERVICE_DEFAULT); // no getter for mFgsDeferBehavior
+
+ // mUsesStandardHeader is not written because it should be recomputed in listeners
+
+ parcel.writeArraySet(allPendingIntents);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
+ public void getActiveNotifications_doesNotLeakAllowlistToken() throws RemoteException {
+ Notification sentFromApp = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentIntent(createPendingIntent("content"))
+ .setPublicVersion(new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentIntent(createPendingIntent("public"))
+ .build())
+ .extend(new Notification.WearableExtender()
+ .addPage(new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentIntent(createPendingIntent("wearPage"))
+ .build()))
+ .build();
+ // Binder transition: app -> NMS
+ Notification receivedByNms = parcelAndUnparcel(sentFromApp, Notification.CREATOR);
+ assertThat(receivedByNms.getAllowlistToken()).isEqualTo(
+ NotificationManagerService.ALLOWLIST_TOKEN);
+ mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", 1,
+ parcelAndUnparcel(receivedByNms, Notification.CREATOR), mUserId);
+ waitForIdle();
+ assertThat(mService.mNotificationList).hasSize(1);
+ Notification posted = mService.mNotificationList.get(0).getNotification();
+ assertThat(posted.getAllowlistToken()).isEqualTo(
+ NotificationManagerService.ALLOWLIST_TOKEN);
+ assertThat(posted.contentIntent.getWhitelistToken()).isEqualTo(
+ NotificationManagerService.ALLOWLIST_TOKEN);
+
+ ParceledListSlice<StatusBarNotification> listSentFromNms =
+ mBinderService.getAppActiveNotifications(mPkg, mUserId);
+ // Binder transition: NMS -> app. App doesn't have the allowlist token so clear it
+ // (having a different one would produce the same effect; the relevant thing is to not let
+ // out ALLOWLIST_TOKEN).
+ // Note: for other tests, this is restored by constructing TestableNMS in setup().
+ Notification.processAllowlistToken = null;
+ ParceledListSlice<StatusBarNotification> listReceivedByApp = parcelAndUnparcel(
+ listSentFromNms, ParceledListSlice.CREATOR);
+ Notification gottenBackByApp = listReceivedByApp.getList().get(0).getNotification();
+
+ assertThat(gottenBackByApp.getAllowlistToken()).isNull();
+ assertThat(gottenBackByApp.contentIntent.getWhitelistToken()).isNull();
+ assertThat(gottenBackByApp.publicVersion.getAllowlistToken()).isNull();
+ assertThat(gottenBackByApp.publicVersion.contentIntent.getWhitelistToken()).isNull();
+ assertThat(new Notification.WearableExtender(gottenBackByApp).getPages()
+ .get(0).getAllowlistToken()).isNull();
+ assertThat(new Notification.WearableExtender(gottenBackByApp).getPages()
+ .get(0).contentIntent.getWhitelistToken()).isNull();
+ }
+
+ @Test
public void enqueueNotification_allowlistsPendingIntents() throws RemoteException {
PendingIntent contentIntent = createPendingIntent("content");
PendingIntent actionIntent1 = createPendingIntent("action1");
@@ -14013,6 +14420,9 @@
eq(REASON_NOTIFICATION_SERVICE), any());
verify(mAmi, times(3)).setPendingIntentAllowBgActivityStarts(any(),
any(), eq(FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER | FLAG_SERVICE_SENDER));
+ contentIntent.cancel();
+ actionIntent2.cancel();
+ actionIntent1.cancel();
}
@Test
@@ -14041,6 +14451,10 @@
eq(REASON_NOTIFICATION_SERVICE), any());
verify(mAmi, times(4)).setPendingIntentAllowBgActivityStarts(any(),
any(), eq(FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER | FLAG_SERVICE_SENDER));
+ contentIntent.cancel();
+ publicContentIntent.cancel();
+ actionIntent.cancel();
+ publicActionIntent.cancel();
}
@Test
@@ -14566,8 +14980,8 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testFixNotification_clearsLifetimeExtendedFlag() throws Exception {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
Notification n = new Notification.Builder(mContext, "test")
.setFlag(FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true)
.build();
@@ -14996,7 +15410,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_ALL_NOTIFS_NEED_TTL)
+ @EnableFlags(FLAG_ALL_NOTIFS_NEED_TTL)
public void testFixNotification_missingTtl() throws Exception {
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -15008,7 +15422,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_ALL_NOTIFS_NEED_TTL)
+ @EnableFlags(FLAG_ALL_NOTIFS_NEED_TTL)
public void testFixNotification_doesNotOverwriteTtl() throws Exception {
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -15026,8 +15440,7 @@
Notification.Builder nb = new Notification.Builder(mContext,
mTestNotificationChannel.getId())
.setFlag(FLAG_USER_INITIATED_JOB, true)
- .setStyle(Notification.CallStyle.forOngoingCall(
- person, mock(PendingIntent.class)))
+ .setStyle(Notification.CallStyle.forOngoingCall(person, mActivityIntent))
.setSmallIcon(android.R.drawable.sym_def_app_icon);
StatusBarNotification sbn = new StatusBarNotification(packageName, packageName, 1,
testName, mUid, 0, nb.build(), userHandle, null, 0);
@@ -15089,25 +15502,27 @@
.thenReturn(false);
}
- private void enqueueToast(String testPackage, ITransientNotification callback)
+ private boolean enqueueToast(String testPackage, ITransientNotification callback)
throws RemoteException {
- enqueueToast((INotificationManager) mService.mService, testPackage, new Binder(), callback);
+ return enqueueToast((INotificationManager) mService.mService, testPackage, new Binder(),
+ callback);
}
- private void enqueueToast(INotificationManager service, String testPackage,
+ private boolean enqueueToast(INotificationManager service, String testPackage,
IBinder token, ITransientNotification callback) throws RemoteException {
- service.enqueueToast(testPackage, token, callback, TOAST_DURATION, /* isUiContext= */ true,
- DEFAULT_DISPLAY);
+ return service.enqueueToast(testPackage, token, callback, TOAST_DURATION, /* isUiContext= */
+ true, DEFAULT_DISPLAY);
}
- private void enqueueTextToast(String testPackage, CharSequence text) throws RemoteException {
- enqueueTextToast(testPackage, text, /* isUiContext= */ true, DEFAULT_DISPLAY);
+ private boolean enqueueTextToast(String testPackage, CharSequence text) throws RemoteException {
+ return enqueueTextToast(testPackage, text, /* isUiContext= */ true, DEFAULT_DISPLAY);
}
- private void enqueueTextToast(String testPackage, CharSequence text, boolean isUiContext,
+ private boolean enqueueTextToast(String testPackage, CharSequence text, boolean isUiContext,
int displayId) throws RemoteException {
- ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(), text,
- TOAST_DURATION, isUiContext, displayId, /* textCallback= */ null);
+ return ((INotificationManager) mService.mService).enqueueTextToast(testPackage,
+ new Binder(), text, TOAST_DURATION, isUiContext, displayId,
+ /* textCallback= */ null);
}
private void mockIsVisibleBackgroundUsersSupported(boolean supported) {
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 aeeca2ae..5033a380 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -3981,7 +3981,7 @@
pm.applicationInfo = new ApplicationInfo();
pm.applicationInfo.uid = UID_O;
List<PackageInfo> packages = ImmutableList.of(pm);
- when(mPm.getInstalledPackagesAsUser(any(), anyInt())).thenReturn(packages);
+ when(mPm.getInstalledPackagesAsUser(eq(0), anyInt())).thenReturn(packages);
mHelper.updateFixedImportance(users);
assertTrue(mHelper.isImportanceLocked(PKG_O, UID_O));
@@ -4097,7 +4097,7 @@
pm.applicationInfo = new ApplicationInfo();
pm.applicationInfo.uid = UID_O;
List<PackageInfo> packages = ImmutableList.of(pm);
- when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages);
+ when(mPm.getInstalledPackagesAsUser(0, 0)).thenReturn(packages);
mHelper.updateFixedImportance(users);
assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
@@ -4120,7 +4120,7 @@
pm.applicationInfo = new ApplicationInfo();
pm.applicationInfo.uid = UID_O;
List<PackageInfo> packages = ImmutableList.of(pm);
- when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages);
+ when(mPm.getInstalledPackagesAsUser(0, 0)).thenReturn(packages);
mHelper.updateFixedImportance(users);
NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
@@ -4309,7 +4309,7 @@
pm.applicationInfo = new ApplicationInfo();
pm.applicationInfo.uid = UID_O;
List<PackageInfo> packages = ImmutableList.of(pm);
- when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages);
+ when(mPm.getInstalledPackagesAsUser(0, 0)).thenReturn(packages);
mHelper.updateFixedImportance(users);
ArraySet<String> toRemove = new ArraySet<>();
@@ -4341,7 +4341,7 @@
pm.applicationInfo = new ApplicationInfo();
pm.applicationInfo.uid = UID_O;
List<PackageInfo> packages = ImmutableList.of(pm);
- when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages);
+ when(mPm.getInstalledPackagesAsUser(0, 0)).thenReturn(packages);
mHelper.updateFixedImportance(users);
assertTrue(mHelper.isImportanceLocked(PKG_O, UID_O));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index ad420f6..527001d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -55,6 +55,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.compat.IPlatformCompat;
import com.android.server.UiServiceTestCase;
import org.junit.Before;
@@ -155,7 +156,8 @@
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new RankingHelper(getContext(), mHandler, mConfig, mMockZenModeHelper,
- mUsageStats, new String[] {ImportanceExtractor.class.getName()});
+ mUsageStats, new String[] {ImportanceExtractor.class.getName()},
+ mock(IPlatformCompat.class));
mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentTitle("A")
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TimeToLiveHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/TimeToLiveHelperTest.java
new file mode 100644
index 0000000..8b46c8c
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TimeToLiveHelperTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static com.android.server.notification.TimeToLiveHelper.EXTRA_KEY;
+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.never;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.SuppressLint;
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.testing.TestableLooper;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import com.android.server.UiServiceTestCase;
+
+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.MockitoAnnotations;
+import org.mockito.quality.Strictness;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
+public class TimeToLiveHelperTest extends UiServiceTestCase {
+
+ TimeToLiveHelper mHelper;
+ @Mock
+ NotificationManagerPrivate mNm;
+ @Mock
+ AlarmManager mAm;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext.addMockSystemService(AlarmManager.class, mAm);
+ mHelper = new TimeToLiveHelper(mNm, mContext);
+ }
+
+ @After
+ public void tearDown() {
+ mHelper.destroy();
+ }
+
+ private NotificationRecord getRecord(String tag, int timeoutAfter) {
+ NotificationChannel channel = new NotificationChannel("id", "name",
+ NotificationManager.IMPORTANCE_HIGH);
+ Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setTimeoutAfter(timeoutAfter);
+
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, tag, mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ return new NotificationRecord(mContext, sbn, channel);
+ }
+
+ @Test
+ public void testTimeout() {
+ mHelper.scheduleTimeoutLocked(getRecord("testTimeout", 1), 1);
+
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(2L), any());
+ assertThat(mHelper.mKeys).hasSize(1);
+ }
+
+ @Test
+ public void testTimeoutExpires() {
+ NotificationRecord r = getRecord("testTimeoutExpires", 1);
+
+ mHelper.scheduleTimeoutLocked(r, 1);
+ ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(2L), captor.capture());
+
+ mHelper.mNotificationTimeoutReceiver.onReceive(mContext, captor.getValue().getIntent());
+
+ assertThat(mHelper.mKeys).isEmpty();
+ }
+
+ @Test
+ public void testTimeoutExpires_twoEntries() {
+ NotificationRecord first = getRecord("testTimeoutFirst", 1);
+ NotificationRecord later = getRecord("testTimeoutSecond", 2);
+
+ mHelper.scheduleTimeoutLocked(first, 1);
+ mHelper.scheduleTimeoutLocked(later, 1);
+
+ ArgumentCaptor<PendingIntent> captorSet = ArgumentCaptor.forClass(PendingIntent.class);
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(2L), captorSet.capture());
+
+ ArgumentCaptor<PendingIntent> captorNewSet = ArgumentCaptor.forClass(PendingIntent.class);
+ mHelper.mNotificationTimeoutReceiver.onReceive(mContext, captorSet.getValue().getIntent());
+
+ assertThat(mHelper.mKeys).hasSize(1);
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(3L), captorNewSet.capture());
+ assertThat(captorSet.getValue().getIntent().getStringExtra(EXTRA_KEY))
+ .isEqualTo(first.getKey());
+ }
+
+ @Test
+ public void testTimeout_earlierEntryAddedSecond() {
+ NotificationRecord later = getRecord("testTimeoutSecond", 2);
+ mHelper.scheduleTimeoutLocked(later, 1);
+
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(3L), any());
+ assertThat(mHelper.mKeys).hasSize(1);
+
+ NotificationRecord first = getRecord("testTimeoutFirst", 1);
+ ArgumentCaptor<PendingIntent> captorSet = ArgumentCaptor.forClass(PendingIntent.class);
+ ArgumentCaptor<PendingIntent> captorCancel = ArgumentCaptor.forClass(PendingIntent.class);
+
+ mHelper.scheduleTimeoutLocked(first, 1);
+
+ assertThat(mHelper.mKeys).hasSize(2);
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(2L), captorSet.capture());
+ assertThat(captorSet.getValue().getIntent().getStringExtra(EXTRA_KEY))
+ .isEqualTo(first.getKey());
+ assertThat(mHelper.mKeys.first().second).isEqualTo(first.getKey());
+
+ verify(mAm).cancel(captorCancel.capture());
+ assertThat(captorCancel.getValue().getIntent().getStringExtra(EXTRA_KEY))
+ .isEqualTo(later.getKey());
+ }
+
+ @Test
+ public void testTimeout_earlierEntryAddedFirst() {
+ NotificationRecord first = getRecord("testTimeoutFirst", 1);
+ NotificationRecord later = getRecord("testTimeoutSecond", 2);
+
+ mHelper.scheduleTimeoutLocked(first, 1);
+ mHelper.scheduleTimeoutLocked(later, 1);
+
+ assertThat(mHelper.mKeys).hasSize(2);
+ assertThat(mHelper.mKeys.first().second).isEqualTo(first.getKey());
+ verify(mAm, never()).cancel((PendingIntent) any());
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(2L), any());
+ }
+
+ @Test
+ public void testTimeout_updateEarliestEntry() {
+ NotificationRecord first = getRecord("testTimeoutFirst", 1);
+
+ mHelper.scheduleTimeoutLocked(first, 1);
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(2L), any());
+
+ NotificationRecord firstUpdated = getRecord("testTimeoutFirst", 3);
+ ArgumentCaptor<PendingIntent> captorSet = ArgumentCaptor.forClass(PendingIntent.class);
+ ArgumentCaptor<PendingIntent> captorCancel = ArgumentCaptor.forClass(PendingIntent.class);
+
+ mHelper.scheduleTimeoutLocked(firstUpdated, 1);
+
+ assertThat(mHelper.mKeys).hasSize(1);
+
+ // cancel original alarm
+ verify(mAm).cancel(captorCancel.capture());
+ assertThat(captorCancel.getValue().getIntent().getStringExtra(EXTRA_KEY))
+ .isEqualTo(first.getKey());
+
+ // schedule later alarm
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(4L), captorSet.capture());
+ assertThat(captorSet.getValue().getIntent().getStringExtra(EXTRA_KEY))
+ .isEqualTo(first.getKey());
+ }
+
+ @Test
+ public void testTimeout_twoEntries_updateEarliestEntry() {
+ NotificationRecord first = getRecord("testTimeoutFirst", 1);
+ NotificationRecord later = getRecord("testTimeoutSecond", 2);
+
+ mHelper.scheduleTimeoutLocked(first, 1);
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(2L), any());
+
+ mHelper.scheduleTimeoutLocked(later, 1);
+
+ NotificationRecord firstUpdated = getRecord("testTimeoutFirst", 3);
+ ArgumentCaptor<PendingIntent> captorSet = ArgumentCaptor.forClass(PendingIntent.class);
+ ArgumentCaptor<PendingIntent> captorCancel = ArgumentCaptor.forClass(PendingIntent.class);
+
+ mHelper.scheduleTimeoutLocked(firstUpdated, 1);
+
+ assertThat(mHelper.mKeys).hasSize(2);
+ assertThat(mHelper.mKeys.first().second).isEqualTo(later.getKey());
+
+ // "first" was canceled because it's now later
+ verify(mAm).cancel(captorCancel.capture());
+ assertThat(captorCancel.getValue().getIntent().getStringExtra(EXTRA_KEY))
+ .isEqualTo(first.getKey());
+
+ // "later" is now the first entry, and needs the matching alarm
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq(3L), captorSet.capture());
+ assertThat(captorSet.getValue().getIntent().getStringExtra(EXTRA_KEY))
+ .isEqualTo(later.getKey());
+ }
+}
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 3df52c7..43f24750 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -3655,6 +3655,7 @@
// Create immersive rule
AutomaticZenRule immersive = new AutomaticZenRule.Builder("Immersed", CONDITION_ID)
.setType(TYPE_IMMERSIVE)
+ .setZenPolicy(mZenModeHelper.mConfig.toZenPolicy()) // same as the manual rule
.build();
String immersiveId = mZenModeHelper.addAutomaticZenRule(mPkg, immersive, UPDATE_ORIGIN_APP,
"reason", CUSTOM_PKG_UID);
@@ -4242,6 +4243,7 @@
public void updateAutomaticZenRule_fromUser_updatesBitmaskAndValue() {
// Adds a starting rule with empty zen policies and device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
+ .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
.setZenPolicy(new ZenPolicy.Builder().build())
.setDeviceEffects(new ZenDeviceEffects.Builder().build())
.build();
@@ -4250,7 +4252,7 @@
azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
- // Modifies the zen policy and device effects
+ // Modifies the filter, zen policy, and device effects
ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy())
.allowPriorityChannels(false)
.build();
diff --git a/services/tests/vibrator/AndroidManifest.xml b/services/tests/vibrator/AndroidManifest.xml
index a14ea55..c0f514f 100644
--- a/services/tests/vibrator/AndroidManifest.xml
+++ b/services/tests/vibrator/AndroidManifest.xml
@@ -30,6 +30,8 @@
<uses-permission android:name="android.permission.ACCESS_VIBRATOR_STATE" />
<!-- Required to set always-on vibrations -->
<uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON" />
+ <!-- Required to play system-only haptic feedback constants -->
+ <uses-permission android:name="android.permission.VIBRATE_SYSTEM_CONSTANTS" />
<application android:debuggable="true">
<uses-library android:name="android.test.mock" android:required="true" />
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index e3d4596..633a3c9 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -27,6 +27,8 @@
import static android.os.VibrationEffect.EFFECT_CLICK;
import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK;
import static android.os.VibrationEffect.EFFECT_TICK;
+import static android.view.HapticFeedbackConstants.BIOMETRIC_CONFIRM;
+import static android.view.HapticFeedbackConstants.BIOMETRIC_REJECT;
import static android.view.HapticFeedbackConstants.CLOCK_TICK;
import static android.view.HapticFeedbackConstants.CONTEXT_CLICK;
import static android.view.HapticFeedbackConstants.KEYBOARD_RELEASE;
@@ -80,6 +82,8 @@
new int[] {SCROLL_ITEM_FOCUS, SCROLL_LIMIT, SCROLL_TICK};
private static final int[] KEYBOARD_FEEDBACK_CONSTANTS =
new int[] {KEYBOARD_TAP, KEYBOARD_RELEASE};
+ private static final int[] BIOMETRIC_FEEDBACK_CONSTANTS =
+ new int[] {BIOMETRIC_CONFIRM, BIOMETRIC_REJECT};
private static final float KEYBOARD_VIBRATION_FIXED_AMPLITUDE = 0.62f;
@@ -283,6 +287,17 @@
}
@Test
+ public void testVibrationAttribute_biometricConstants_returnsCommunicationRequestUsage() {
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : BIOMETRIC_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false, /* fromIme= */ false);
+ assertThat(attrs.getUsage()).isEqualTo(VibrationAttributes.USAGE_COMMUNICATION_REQUEST);
+ }
+ }
+
+ @Test
public void testVibrationAttribute_forNotBypassingIntensitySettings() {
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
@@ -422,6 +437,15 @@
}
}
+ @Test
+ public void testIsRestricted_biometricConstants_returnsTrue() {
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : BIOMETRIC_FEEDBACK_CONSTANTS) {
+ assertThat(hapticProvider.isRestrictedHapticFeedback(effectId)).isTrue();
+ }
+ }
+
private HapticFeedbackVibrationProvider createProviderWithDefaultCustomizations() {
return createProvider(/* customizations= */ null);
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 3f5217c..8ca8623 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -39,6 +39,7 @@
import android.content.ComponentName;
import android.content.pm.PackageManagerInternal;
import android.frameworks.vibrator.ScaleParam;
+import android.frameworks.vibrator.VibrationParam;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -59,6 +60,8 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -177,6 +180,24 @@
verifyZeroInteractions(mMockVibrationScaler);
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testOnRequestVibrationParamsComplete_withNullVibrationParams_throwsException() {
+ mVibratorControlService.registerVibratorController(mFakeVibratorController);
+ int timeoutInMillis = 10;
+ CompletableFuture<Void> unusedFuture =
+ mVibratorControlService.triggerVibrationParamsRequest(UID, USAGE_RINGTONE,
+ timeoutInMillis);
+ IBinder token = mVibratorControlService.getRequestVibrationParamsToken();
+
+ List<VibrationParam> vibrationParamList = Arrays.asList(
+ VibrationParamGenerator.generateVibrationParam(ScaleParam.TYPE_ALARM, 0.7f),
+ null,
+ VibrationParamGenerator.generateVibrationParam(ScaleParam.TYPE_NOTIFICATION, 0.4f));
+
+ mVibratorControlService.onRequestVibrationParamsComplete(token,
+ vibrationParamList.toArray(new VibrationParam[0]));
+ }
+
@Test
public void testSetVibrationParams_cachesAdaptiveHapticsScalesCorrectly() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
@@ -214,6 +235,19 @@
verifyZeroInteractions(mMockVibrationScaler);
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetVibrationParams_withNullVibrationParams_throwsException() {
+ mVibratorControlService.registerVibratorController(mFakeVibratorController);
+ List<VibrationParam> vibrationParamList = Arrays.asList(
+ VibrationParamGenerator.generateVibrationParam(ScaleParam.TYPE_ALARM, 0.7f),
+ null,
+ VibrationParamGenerator.generateVibrationParam(ScaleParam.TYPE_NOTIFICATION, 0.4f));
+
+ mVibratorControlService.setVibrationParams(
+ vibrationParamList.toArray(new VibrationParam[0]),
+ mFakeVibratorController);
+ }
+
@Test
public void testClearVibrationParams_clearsCachedAdaptiveHapticsScales() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 185677f..d6c0fef 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -1410,6 +1410,70 @@
}
@Test
+ public void performHapticFeedback_restrictedConstantsWithoutPermission_doesNotVibrate()
+ throws Exception {
+ // Deny permission to vibrate with restricted constants
+ denyPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS);
+ // Public constant, no permission required
+ mHapticFeedbackVibrationMap.put(
+ HapticFeedbackConstants.CONFIRM,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+ // Hidden system-only constant, permission required
+ mHapticFeedbackVibrationMap.put(
+ HapticFeedbackConstants.BIOMETRIC_CONFIRM,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK));
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setSupportedEffects(
+ VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_HEAVY_CLICK);
+ VibratorManagerService service = createSystemReadyService();
+
+ performHapticFeedbackAndWaitUntilFinished(
+ service, HapticFeedbackConstants.CONFIRM, /* always= */ false);
+
+ performHapticFeedbackAndWaitUntilFinished(
+ service, HapticFeedbackConstants.BIOMETRIC_CONFIRM, /* always= */ false);
+
+ List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments();
+ assertEquals(1, playedSegments.size());
+ PrebakedSegment segment = (PrebakedSegment) playedSegments.get(0);
+ assertEquals(VibrationEffect.EFFECT_CLICK, segment.getEffectId());
+ }
+
+ @Test
+ public void performHapticFeedback_restrictedConstantsWithPermission_playsVibration()
+ throws Exception {
+ // Grant permission to vibrate with restricted constants
+ grantPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS);
+ // Public constant, no permission required
+ mHapticFeedbackVibrationMap.put(
+ HapticFeedbackConstants.CONFIRM,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+ // Hidden system-only constant, permission required
+ mHapticFeedbackVibrationMap.put(
+ HapticFeedbackConstants.BIOMETRIC_CONFIRM,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK));
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setSupportedEffects(
+ VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_HEAVY_CLICK);
+ VibratorManagerService service = createSystemReadyService();
+
+ performHapticFeedbackAndWaitUntilFinished(
+ service, HapticFeedbackConstants.CONFIRM, /* always= */ false);
+
+ performHapticFeedbackAndWaitUntilFinished(
+ service, HapticFeedbackConstants.BIOMETRIC_CONFIRM, /* always= */ false);
+
+ List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments();
+ assertEquals(2, playedSegments.size());
+ assertEquals(VibrationEffect.EFFECT_CLICK,
+ ((PrebakedSegment) playedSegments.get(0)).getEffectId());
+ assertEquals(VibrationEffect.EFFECT_HEAVY_CLICK,
+ ((PrebakedSegment) playedSegments.get(1)).getEffectId());
+ }
+
+ @Test
public void performHapticFeedback_doesNotVibrateWhenVibratorInfoNotReady() throws Exception {
denyPermission(android.Manifest.permission.VIBRATE);
mHapticFeedbackVibrationMap.put(
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java b/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java
index a606388..c17d11e 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java
@@ -42,7 +42,10 @@
return vibrationParamList.toArray(new VibrationParam[0]);
}
- private static VibrationParam generateVibrationParam(int type, float scale) {
+ /**
+ * Generates a {@link VibrationParam} with the specified type and scale.
+ */
+ public static VibrationParam generateVibrationParam(int type, float scale) {
ScaleParam scaleParam = new ScaleParam();
scaleParam.typesMask = type;
scaleParam.scale = scale;
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 2c88ed2..7356b43 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1717,6 +1717,7 @@
// The display should be rotated after the launch is finished.
doReturn(false).when(app).isAnimating(anyInt(), anyInt());
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
+ waitHandlerIdle(mWm.mH);
mStatusBarWindow.finishSeamlessRotation(t);
mNavBarWindow.finishSeamlessRotation(t);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
index 80e169d..b90fa21 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
@@ -25,6 +25,8 @@
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
+import static com.android.server.wm.testing.Assert.assertThrows;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -37,6 +39,8 @@
import androidx.test.filters.SmallTest;
+import com.android.server.wm.testing.Assert;
+
import org.junit.Before;
import org.junit.Test;
@@ -288,4 +292,56 @@
false /* forTabletopMode */,
LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
}
+
+ @Test
+ public void test_setLetterboxHorizontalPositionMultiplier_validValues() {
+ assertThrows(IllegalArgumentException.class,
+ () -> mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(-1));
+ assertThrows(IllegalArgumentException.class,
+ () -> mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(2));
+
+ // Does not throw an exception for values [0,1].
+ mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(0);
+ mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(0.5f);
+ mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(1);
+ }
+
+ @Test
+ public void test_setLetterboxVerticalPositionMultiplier_validValues() {
+ assertThrows(IllegalArgumentException.class,
+ () -> mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(-1));
+ assertThrows(IllegalArgumentException.class,
+ () -> mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(2));
+
+ // Does not throw an exception for values [0,1].
+ mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0);
+ mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
+ mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(1);
+ }
+
+ @Test
+ public void test_setLetterboxBookModePositionMultiplier_validValues() {
+ assertThrows(IllegalArgumentException.class,
+ () -> mLetterboxConfiguration.setLetterboxBookModePositionMultiplier(-1));
+ assertThrows(IllegalArgumentException.class,
+ () -> mLetterboxConfiguration.setLetterboxBookModePositionMultiplier(2));
+
+ // Does not throw an exception for values [0,1].
+ mLetterboxConfiguration.setLetterboxBookModePositionMultiplier(0);
+ mLetterboxConfiguration.setLetterboxBookModePositionMultiplier(0.5f);
+ mLetterboxConfiguration.setLetterboxBookModePositionMultiplier(1);
+ }
+
+ @Test
+ public void test_setLetterboxTabletopModePositionMultiplier_validValues() {
+ assertThrows(IllegalArgumentException.class,
+ () -> mLetterboxConfiguration.setLetterboxTabletopModePositionMultiplier(-1));
+ assertThrows(IllegalArgumentException.class,
+ () -> mLetterboxConfiguration.setLetterboxTabletopModePositionMultiplier(2));
+
+ // Does not throw an exception for values [0,1].
+ mLetterboxConfiguration.setLetterboxTabletopModePositionMultiplier(0);
+ mLetterboxConfiguration.setLetterboxTabletopModePositionMultiplier(0.5f);
+ mLetterboxConfiguration.setLetterboxTabletopModePositionMultiplier(1);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 649f520..dcf3dad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -74,6 +74,7 @@
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.PowerManager;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
@@ -237,6 +238,24 @@
assertFalse(wpc.hasActivities());
}
+ @Test
+ public void testAttachApplication() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ activity.detachFromProcess();
+ mAtm.startProcessAsync(activity, false /* knownToBeDead */,
+ true /* isTop */, "test" /* hostingType */);
+ final WindowProcessController proc = mSystemServicesTestRule.addProcess(
+ activity.packageName, activity.processName,
+ 6789 /* pid */, activity.info.applicationInfo.uid);
+ try {
+ mRootWindowContainer.attachApplication(proc);
+ verify(mSupervisor).realStartActivityLocked(eq(activity), eq(proc), anyBoolean(),
+ anyBoolean());
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
/**
* This test ensures that we do not try to restore a task based off an invalid task id. We
* should expect {@code null} to be returned in this case.
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 856ad2a..aa780ed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -2363,6 +2363,92 @@
}
@Test
+ public void testUserOverrideFullscreenForLandscapeDisplay() {
+ final int displayWidth = 1600;
+ final int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ spyOn(mActivity.mWmService.mLetterboxConfiguration);
+ doReturn(true).when(mActivity.mWmService.mLetterboxConfiguration)
+ .isUserAppAspectRatioFullscreenEnabled();
+
+ // Set user aspect ratio override
+ spyOn(mActivity.mLetterboxUiController);
+ doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN).when(mActivity.mLetterboxUiController)
+ .getUserMinAspectRatioOverrideCode();
+
+ prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect bounds = mActivity.getBounds();
+
+ // bounds should be fullscreen
+ assertEquals(displayHeight, bounds.height());
+ assertEquals(displayWidth, bounds.width());
+ }
+
+ @Test
+ public void testUserOverrideFullscreenForPortraitDisplay() {
+ final int displayWidth = 1400;
+ final int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ spyOn(mActivity.mWmService.mLetterboxConfiguration);
+ doReturn(true).when(mActivity.mWmService.mLetterboxConfiguration)
+ .isUserAppAspectRatioFullscreenEnabled();
+
+ // Set user aspect ratio override
+ spyOn(mActivity.mLetterboxUiController);
+ doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN).when(mActivity.mLetterboxUiController)
+ .getUserMinAspectRatioOverrideCode();
+
+ prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_LANDSCAPE);
+
+ final Rect bounds = mActivity.getBounds();
+
+ // bounds should be fullscreen
+ assertEquals(displayHeight, bounds.height());
+ assertEquals(displayWidth, bounds.width());
+ }
+
+ @Test
+ public void testSystemFullscreenOverrideForLandscapeDisplay() {
+ final int displayWidth = 1600;
+ final int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ spyOn(mActivity.mLetterboxUiController);
+ doReturn(true).when(mActivity.mLetterboxUiController)
+ .isSystemOverrideToFullscreenEnabled();
+
+ prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect bounds = mActivity.getBounds();
+
+ // bounds should be fullscreen
+ assertEquals(displayHeight, bounds.height());
+ assertEquals(displayWidth, bounds.width());
+ }
+
+ @Test
+ public void testSystemFullscreenOverrideForPortraitDisplay() {
+ final int displayWidth = 1400;
+ final int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ spyOn(mActivity.mLetterboxUiController);
+ doReturn(true).when(mActivity.mLetterboxUiController)
+ .isSystemOverrideToFullscreenEnabled();
+
+ prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_LANDSCAPE);
+
+ final Rect bounds = mActivity.getBounds();
+
+ // bounds should be fullscreen
+ assertEquals(displayHeight, bounds.height());
+ assertEquals(displayWidth, bounds.width());
+ }
+
+ @Test
public void testUserOverrideSplitScreenAspectRatioForLandscapeDisplay() {
final int displayWidth = 1600;
final int displayHeight = 1400;
@@ -4056,31 +4142,6 @@
}
@Test
- public void testUpdateResolvedBoundsHorizontalPosition_invalidMultiplier_defaultToCenter() {
- // Display configured as (2800, 1400).
-
- // Below 0.0.
- assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
- /* letterboxHorizontalPositionMultiplier */ -1.0f,
- // At launch.
- /* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
- // After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
- // After the display is resized to (700, 1400).
- /* sizeCompatScaled */ new Rect(525, 0, 875, 700));
-
- // Above 1.0
- assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
- /* letterboxHorizontalPositionMultiplier */ 2.0f,
- // At launch.
- /* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
- // After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
- // After the display is resized to (700, 1400).
- /* sizeCompatScaled */ new Rect(525, 0, 875, 700));
- }
-
- @Test
public void testUpdateResolvedBoundsHorizontalPosition_right() {
// Display configured as (2800, 1400).
assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
@@ -4117,12 +4178,8 @@
}
@Test
- public void testPortraitCloseToSquareDisplayWithTaskbar_notLetterboxed() {
- if (Flags.insetsDecoupledConfiguration()) {
- // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
- // bounds no longer contains display cutout.
- return;
- }
+ @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
+ public void testPortraitCloseToSquareDisplayWithTaskbar_letterboxed() {
// Set up portrait close to square display
setUpDisplaySizeWithApp(2200, 2280);
final DisplayContent display = mActivity.mDisplayContent;
@@ -4135,16 +4192,21 @@
.setInsetsSize(Insets.of(0, 0, 0, 150))
};
display.getDisplayPolicy().addWindowLw(navbar, navbar.mAttrs);
- assertTrue(navbar.providesDisplayDecorInsets()
- && display.getDisplayPolicy().updateDecorInsetsInfo());
+ assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo());
display.sendNewConfiguration();
- prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
- // Activity is fullscreen even though orientation is not respected with insets, because
- // the display still matches or is less than the activity aspect ratio
- assertEquals(display.getBounds(), mActivity.getBounds());
- assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ final Rect bounds = activity.getBounds();
+ // Activity should be letterboxed and should have portrait app bounds
+ assertTrue(activity.isLetterboxedForFixedOrientationAndAspectRatio());
+ assertTrue(bounds.height() > bounds.width());
}
@Test
@@ -4166,6 +4228,7 @@
// can be aligned inside parentAppBounds
assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 2200));
}
+
@Test
public void testApplyAspectRatio_activityCannotAlignWithParentAppVertical() {
if (Flags.insetsDecoupledConfiguration()) {
@@ -4310,31 +4373,6 @@
}
@Test
- public void testUpdateResolvedBoundsVerticalPosition_invalidMultiplier_defaultToCenter() {
- // Display configured as (1400, 2800).
-
- // Below 0.0.
- assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
- /* letterboxVerticalPositionMultiplier */ -1.0f,
- // At launch.
- /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750),
- // After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050),
- // After the display is resized to (1400, 700).
- /* sizeCompatScaled */ new Rect(0, 525, 700, 875));
-
- // Above 1.0
- assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
- /* letterboxVerticalPositionMultiplier */ 2.0f,
- // At launch.
- /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750),
- // After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050),
- // After the display is resized to (1400, 700).
- /* sizeCompatScaled */ new Rect(0, 525, 700, 875));
- }
-
- @Test
public void testUpdateResolvedBoundsVerticalPosition_bottom() {
// Display configured as (1400, 2800).
assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 413d003..b9fe074 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -68,6 +68,7 @@
import android.os.PowerSaveState;
import android.os.StrictMode;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.DeviceConfig;
import android.util.Log;
import android.view.DisplayInfo;
@@ -194,6 +195,7 @@
mMockitoSession = mockitoSession()
.mockStatic(LocalServices.class, spyStubOnly)
.mockStatic(DeviceConfig.class, spyStubOnly)
+ .mockStatic(UserManager.class, spyStubOnly)
.mockStatic(SurfaceControl.class, mockStubOnly)
.mockStatic(DisplayControl.class, mockStubOnly)
.mockStatic(LockGuard.class, mockStubOnly)
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 52485ee..002a3d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -19,6 +19,8 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -1024,6 +1026,58 @@
}
@Test
+ public void testApplyTransaction_createTaskFragment_overrideOrientation_systemOrganizer() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+ mController.unregisterOrganizer(mIOrganizer);
+ registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
+
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord activity = createActivityRecord(task);
+ final int uid = Binder.getCallingUid();
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid;
+ final IBinder fragmentToken = new Binder();
+
+ // Create a TaskFragment with OverrideOrientation set.
+ final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
+ mOrganizerToken, fragmentToken, activity.token)
+ .setOverrideOrientation(SCREEN_ORIENTATION_BEHIND)
+ .build();
+ mTransaction.setTaskFragmentOrganizer(mIOrganizer);
+ mTransaction.createTaskFragment(params);
+ assertApplyTransactionAllowed(mTransaction);
+
+ // TaskFragment override orientation should be set for a system organizer.
+ final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(fragmentToken);
+ assertNotNull(taskFragment);
+ assertEquals(SCREEN_ORIENTATION_BEHIND, taskFragment.getOverrideOrientation());
+ }
+
+ @Test
+ public void testApplyTransaction_createTaskFragment_overrideOrientation_nonSystemOrganizer() {
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord activity = createActivityRecord(task);
+ final int uid = Binder.getCallingUid();
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid;
+ final IBinder fragmentToken = new Binder();
+
+ // Create a TaskFragment with OverrideOrientation set.
+ final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
+ mOrganizerToken, fragmentToken, activity.token)
+ .setOverrideOrientation(SCREEN_ORIENTATION_BEHIND)
+ .build();
+ mTransaction.setTaskFragmentOrganizer(mIOrganizer);
+ mTransaction.createTaskFragment(params);
+ assertApplyTransactionAllowed(mTransaction);
+
+ // TaskFragment override orientation is ignored for a non-system organizer.
+ final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(fragmentToken);
+ assertNotNull(taskFragment);
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, taskFragment.getOverrideOrientation());
+ }
+
+ @Test
public void testApplyTransaction_reparentActivityToTaskFragment_triggerLifecycleUpdate() {
final Task task = createTask(mDisplayContent);
final ActivityRecord activity = createActivityRecord(task);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 3c5b12c..4837fcb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -22,6 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -752,6 +753,21 @@
}
@Test
+ public void testGetOrientation_reportOverrideOrientation() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment tf = createTaskFragmentWithActivity(task);
+ final ActivityRecord activity = tf.getTopMostActivity();
+ tf.setOverrideOrientation(SCREEN_ORIENTATION_BEHIND);
+
+ // Should report the override orientation
+ assertEquals(SCREEN_ORIENTATION_BEHIND, tf.getOrientation(SCREEN_ORIENTATION_UNSET));
+
+ // Should report the override orientation even if the activity requests a different value
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ assertEquals(SCREEN_ORIENTATION_BEHIND, tf.getOrientation(SCREEN_ORIENTATION_UNSET));
+ }
+
+ @Test
public void testUpdateImeParentForActivityEmbedding() {
// Setup two activities in ActivityEmbedding.
final Task task = createTask(mDisplayContent);
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 1ca808f..225e85e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -833,8 +833,11 @@
final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
final ActivityRecord.CompatDisplayInsets compatInsets =
new ActivityRecord.CompatDisplayInsets(
- display, activity, /* fixedOrientationBounds= */ null);
- task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatInsets);
+ display, activity, /* letterboxedContainerBounds */ null,
+ /* useOverrideInsets */ false);
+ final TaskFragment.ConfigOverrideHint overrideHint = new TaskFragment.ConfigOverrideHint();
+ overrideHint.mTmpCompatInsets = compatInsets;
+ task.computeConfigResourceOverrides(inOutConfig, parentConfig, overrideHint);
assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds());
final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 0186006..42fe3a7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1630,6 +1630,7 @@
assertTrue(controller.mWaitingTransitions.contains(transition));
assertTrue(controller.isTransientHide(appTask));
assertTrue(controller.isTransientVisible(appTask));
+ assertTrue(controller.isTransientLaunch(recent));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 72bedf2..5b1a18d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -410,6 +410,8 @@
final WindowState wallpaperWindow = createWallpaperWindow(dc);
final WallpaperWindowToken token = wallpaperWindow.mToken.asWallpaperToken();
wallpaperWindow.setHasSurface(true);
+ spyOn(dc.mWallpaperController);
+ doReturn(wallpaperWindow).when(dc.mWallpaperController).getWallpaperTarget();
// Set-up mock shell transitions
registerTestTransitionPlayer();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index ec2c968..5fe71a1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -25,7 +25,6 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_OWN_FOCUS;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SENSITIVE_FOR_TRACING;
@@ -38,6 +37,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -82,6 +82,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -1162,7 +1163,8 @@
invocationOnMock.callRealMethod();
return null;
}).when(surface).lockCanvas(any());
- mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId);
+ mWm.mAccessibilityController
+ .recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded(displayId);
waitUntilHandlersIdle();
try {
verify(surface).lockCanvas(any());
@@ -1170,7 +1172,8 @@
clearInvocations(surface);
// Invalidate and redraw.
mWm.mAccessibilityController.onDisplaySizeChanged(mDisplayContent);
- mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId);
+ mWm.mAccessibilityController
+ .recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded(displayId);
// Turn off magnification to release surface.
mWm.mAccessibilityController.setMagnificationCallbacks(displayId, null);
waitUntilHandlersIdle();
@@ -1302,7 +1305,8 @@
}
@Test
- public void testAddOverlayWindowToUnassignedDisplay_notAllowed() {
+ public void testAddOverlayWindowToUnassignedDisplay_notAllowed_ForVisibleBackgroundUsers() {
+ doReturn(true).when(() -> UserManager.isVisibleBackgroundUsersEnabled());
int uid = 100000; // uid for non-system user
Session session = createTestSession(mAtm, 1234 /* pid */, uid);
DisplayContent dc = createNewDisplay();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 43b424f..69b5c37 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -1681,7 +1681,8 @@
WindowContainerToken wct = rootTask.mRemoteToken.toWindowContainerToken();
t.setWindowingMode(wct, WINDOWING_MODE_PINNED);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
- verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
+ verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivitiesUnchecked(any(),
+ any(), any(), anyBoolean());
clearInvocations(mWm.mAtmService.mRootWindowContainer);
// The token for the PIP root task may have changed when the task entered PIP mode, so do
@@ -1690,7 +1691,8 @@
record.getRootTask().mRemoteToken.toWindowContainerToken();
t.setWindowingMode(newToken, WINDOWING_MODE_FULLSCREEN);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
- verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
+ verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivitiesUnchecked(any(),
+ any(), any(), anyBoolean());
}
@Test
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index ae4faa8..9729c68 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -180,7 +180,8 @@
LocalServices.getService(ActivityManagerInternal.class));
mAtmInternal = Objects.requireNonNull(
LocalServices.getService(ActivityTaskManagerInternal.class));
- mWmInternal = LocalServices.getService(WindowManagerInternal.class);
+ mWmInternal = Objects.requireNonNull(
+ LocalServices.getService(WindowManagerInternal.class));
mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService(
LegacyPermissionManagerInternal.class);
@@ -2737,12 +2738,8 @@
isManagedProfileVisible = true;
}
}
- final ScreenCapture.ScreenshotHardwareBuffer shb;
- if (mWmInternal != null) {
- shb = mWmInternal.takeAssistScreenshot();
- } else {
- shb = null;
- }
+ final ScreenCapture.ScreenshotHardwareBuffer shb =
+ mWmInternal.takeAssistScreenshot();
final Bitmap bm = shb != null ? shb.asBitmap() : null;
// Now that everything is fetched, putting it in the launchIntent.
if (bm != null) {
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index ec60c67..0ddc38a 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -35,8 +35,6 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.flags.FeatureFlags;
-import com.android.internal.telephony.flags.FeatureFlagsImpl;
import java.util.HashMap;
import java.util.HashSet;
@@ -48,8 +46,7 @@
private static final String LOG_TAG = "TelephonyPermissions";
private static final boolean DBG = false;
- /** Feature flags */
- private static final FeatureFlags sFeatureFlag = new FeatureFlagsImpl();
+
/**
* Whether to disable the new device identifier access restrictions.
*/
@@ -886,12 +883,6 @@
*/
public static boolean checkSubscriptionAssociatedWithUser(@NonNull Context context, int subId,
@NonNull UserHandle callerUserHandle) {
- if (!sFeatureFlag.rejectBadSubIdInteraction()
- && !SubscriptionManager.isValidSubscriptionId(subId)) {
- // Return true for invalid sub Id.
- return true;
- }
-
SubscriptionManager subManager = (SubscriptionManager) context.getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
final long token = Binder.clearCallingIdentity();
@@ -906,7 +897,7 @@
} catch (IllegalArgumentException e) {
// Found no record of this sub Id.
Log.e(LOG_TAG, "Subscription[Subscription ID:" + subId + "] has no records on device");
- return !sFeatureFlag.rejectBadSubIdInteraction();
+ return false;
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 7b5b07c..f31a87f 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -349,15 +349,16 @@
}
/**
- * @param plmn target plmn for validation.
- * @return {@code true} if the target plmn is valid {@code false} otherwise.
+ * @param input string that want to be compared.
+ * @param regex string that express regular expression
+ * @return {@code true} if matched {@code false} otherwise.
*/
- public static boolean isValidPlmn(@Nullable String plmn) {
- if (TextUtils.isEmpty(plmn)) {
+ private static boolean isValidPattern(@Nullable String input, @Nullable String regex) {
+ if (TextUtils.isEmpty(input) || TextUtils.isEmpty(regex)) {
return false;
}
- Pattern pattern = Pattern.compile("^(?:[0-9]{3})(?:[0-9]{2}|[0-9]{3})$");
- Matcher matcher = pattern.matcher(plmn);
+ Pattern pattern = Pattern.compile(regex);
+ Matcher matcher = pattern.matcher(input);
if (!matcher.matches()) {
return false;
}
@@ -365,6 +366,22 @@
}
/**
+ * @param countryCode two letters country code based on the ISO 3166-1.
+ * @return {@code true} if the countryCode is valid {@code false} otherwise.
+ */
+ public static boolean isValidCountryCode(@Nullable String countryCode) {
+ return isValidPattern(countryCode, "^[A-Za-z]{2}$");
+ }
+
+ /**
+ * @param plmn target plmn for validation.
+ * @return {@code true} if the target plmn is valid {@code false} otherwise.
+ */
+ public static boolean isValidPlmn(@Nullable String plmn) {
+ return isValidPattern(plmn, "^(?:[0-9]{3})(?:[0-9]{2}|[0-9]{3})$");
+ }
+
+ /**
* @param serviceType target serviceType for validation.
* @return {@code true} if the target serviceType is valid {@code false} otherwise.
*/
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0a8a18dc..f076de3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -289,7 +289,7 @@
@SystemApi
public static final int RADIO_POWER_REASON_NEARBY_DEVICE = 3;
- /** The otaspMode passed to PhoneStateListener#onOtaspChanged */
+ /** The otaspMode passed to SercvieState changes */
/** @hide */
static public final int OTASP_UNINITIALIZED = 0;
/** @hide */
@@ -995,9 +995,9 @@
"android.intent.action.CALL_DISCONNECT_CAUSE";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
- * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
- * containing the disconnect cause.
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and {@link
+ * TelephonyCallback.PreciseCallStateListener#onPreciseCallStateChanged(PreciseCallState)} for
+ * an integer containing the disconnect cause.
*
* @see DisconnectCause
*
@@ -1012,9 +1012,9 @@
public static final String EXTRA_DISCONNECT_CAUSE = "disconnect_cause";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
- * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
- * containing the disconnect cause provided by the RIL.
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and {@link
+ * TelephonyCallback.PreciseCallStateListener#onPreciseCallStateChanged(PreciseCallState)} for
+ * an integer containing the disconnect cause provided by the RIL.
*
* @see PreciseDisconnectCause
*
@@ -6437,8 +6437,8 @@
* This method considers not only calls in the Telephony stack, but also calls via other
* {@link android.telecom.ConnectionService} implementations.
* <p>
- * Note: The call state returned via this method may differ from what is reported by
- * {@link PhoneStateListener#onCallStateChanged(int, String)}, as that callback only considers
+ * Note: The call state returned via this method may differ from what is reported by {@link
+ * TelephonyCallback.CallStateListener#onCallStateChanged(int)}, as that callback only considers
* Telephony (mobile) calls.
* <p>
* Requires Permission:
@@ -6996,7 +6996,7 @@
*
* <p>Beginning with {@link android.os.Build.VERSION_CODES#Q Android Q},
* if this API results in a change of the cached CellInfo, that change will be reported via
- * {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()}.
+ * {@link TelephonyCallback.CellInfoListener#onCellInfoChanged(List) onCellInfoChanged()}.
*
* <p>Apps targeting {@link android.os.Build.VERSION_CODES#Q Android Q} or higher will no
* longer trigger a refresh of the cached CellInfo by invoking this API. Instead, those apps
@@ -7109,7 +7109,7 @@
* camped/registered, serving, and neighboring cells.
*
* <p>Any available results from this request will be provided by calls to
- * {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()}
+ * {@link TelephonyCallback.CellInfoListener#onCellInfoChanged(List) onCellInfoChanged()}
* for each active subscription.
*
* <p>This method returns valid data for devices with
@@ -7172,7 +7172,7 @@
* camped/registered, serving, and neighboring cells.
*
* <p>Any available results from this request will be provided by calls to
- * {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()}
+ * {@link TelephonyCallback.CellInfoListener#onCellInfoChanged(List) onCellInfoChanged()}
* for each active subscription.
*
* <p>This method returns valid data for devices with
@@ -7248,8 +7248,8 @@
}
/**
- * Sets the minimum time in milli-seconds between {@link PhoneStateListener#onCellInfoChanged
- * PhoneStateListener.onCellInfoChanged} will be invoked.
+ * Sets the minimum time in milli-seconds between {@link
+ * TelephonyCallback.CellInfoListener#onCellInfoChanged(List)} will be invoked.
*<p>
* The default, 0, means invoke onCellInfoChanged when any of the reported
* information changes. Setting the value to INT_MAX(0x7fffffff) means never issue
@@ -11364,7 +11364,7 @@
* Shut down all the live radios over all the slot indexes.
*
* <p>To know when the radio has completed powering off, use
- * {@link PhoneStateListener#LISTEN_SERVICE_STATE LISTEN_SERVICE_STATE}.
+ * {@link TelephonyCallback.ServiceStateListener}.
*
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
@@ -13058,8 +13058,9 @@
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
- * If you want continuous updates of service state info, register a {@link PhoneStateListener}
- * via {@link #listen} with the {@link PhoneStateListener#LISTEN_SERVICE_STATE} event.
+ * If you want continuous updates of service state info, register a {@link TelephonyCallback}
+ * that implements {@link TelephonyCallback.ServiceStateListener} through {@link
+ * #registerTelephonyCallback}.
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
@@ -13086,8 +13087,9 @@
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
- * If you want continuous updates of service state info, register a {@link PhoneStateListener}
- * via {@link #listen} with the {@link PhoneStateListener#LISTEN_SERVICE_STATE} event.
+ * If you want continuous updates of service state info, register a {@link TelephonyCallback}
+ * that implements {@link TelephonyCallback.ServiceStateListener} through {@link
+ * #registerTelephonyCallback}.
*
* There's another way to renounce permissions with a custom context
* {@code AttributionSource.Builder#setRenouncedPermissions(Set<String>)} but only for system
@@ -17892,9 +17894,9 @@
* measurements breach the specified thresholds.
*
* To be notified, set the signal strength update request and then register
- * {@link TelephonyManager#listen(PhoneStateListener, int)} with
- * {@link PhoneStateListener#LISTEN_SIGNAL_STRENGTHS}. The notification will arrive through
- * {@link PhoneStateListener#onSignalStrengthsChanged(SignalStrength)}.
+ * {@link TelephonyCallback} that implements {@link TelephonyCallback.SignalStrengthsListener}
+ * through {@link #registerTelephonyCallback}. The notification will arrive through
+ * {@link TelephonyCallback.SignalStrengthsListener#onSignalStrengthsChanged(SignalStrength)}.
*
* To stop receiving the notification over the specified thresholds, pass the same
* {@link SignalStrengthUpdateRequest} object to
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index ebabbf9..ca4a643 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -1039,17 +1039,18 @@
* subscription on the
* current eUICC and the subscription to be downloaded according to the subscription metadata.
* Without the former, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be
- * eturned in the callback intent to prompt the user to accept the download.
+ * returned in the callback intent to prompt the user to accept the download.
*
* <p> Starting from Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM},
* if the caller has the
* {@code android.Manifest.permission#MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS} permission or
- * is a profile owner or device owner, and
- * {@code switchAfterDownload} is {@code false}, then the downloaded subscription
- * will be managed by that caller. If {@code switchAfterDownload} is true,
- * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be
- * returned in the callback intent to prompt the user to accept the download and the
- * subscription will not be managed.
+ * is a profile owner or device owner, then the downloaded subscription
+ * will be managed by that caller.
+ * In case the caller is device owner or profile owner of an organization-owned device, {@code
+ * switchAfterDownload} can be set to true to automatically enable the subscription after
+ * download. If the caller is a profile owner on non organization owned device
+ * {@code switchAfterDownload} should be false otherwise the operation will fail with
+ * {@link #EMBEDDED_SUBSCRIPTION_RESULT_ERROR}.
*
* <p>On a multi-active SIM device, requires the
* {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or a calling app
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
index 46ad77e..519b429 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.activityembedding.close
+import android.graphics.Rect
import android.platform.test.annotations.Presubmit
-import android.tools.datatypes.Rect
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -122,7 +122,7 @@
companion object {
/** {@inheritDoc} */
- private var startDisplayBounds = Rect.EMPTY
+ private var startDisplayBounds = Rect()
/**
* Creates the test configurations.
*
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
index af4f7a7..4cd6d15b 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.activityembedding.layoutchange
+import android.graphics.Rect
import android.platform.test.annotations.Presubmit
-import android.tools.datatypes.Rect
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -114,11 +114,11 @@
// Compare dimensions of two splits, given we're using default split attributes,
// both activities take up the same visible size on the display.
check { "height" }
- .that(topLayerRegion.region.height)
- .isEqual(bottomLayerRegion.region.height)
+ .that(topLayerRegion.region.bounds.height())
+ .isEqual(bottomLayerRegion.region.bounds.height())
check { "width" }
- .that(topLayerRegion.region.width)
- .isEqual(bottomLayerRegion.region.width)
+ .that(topLayerRegion.region.bounds.width())
+ .isEqual(bottomLayerRegion.region.bounds.width())
topLayerRegion.notOverlaps(bottomLayerRegion.region)
// Layers of two activities sum to be fullscreen size on display.
topLayerRegion.plus(bottomLayerRegion.region).coversExactly(startDisplayBounds)
@@ -132,14 +132,17 @@
// Compare dimensions of two splits, given we're using default split attributes,
// both activities take up the same visible size on the display.
check { "height" }
- .that(topLayerRegion.region.height)
- .isLower(bottomLayerRegion.region.height)
+ .that(topLayerRegion.region.bounds.height())
+ .isLower(bottomLayerRegion.region.bounds.height())
check { "height" }
- .that(topLayerRegion.region.height / 0.3f - bottomLayerRegion.region.height / 0.7f)
+ .that(
+ topLayerRegion.region.bounds.height() / 0.3f -
+ bottomLayerRegion.region.bounds.height() / 0.7f
+ )
.isLower(0.1f)
check { "width" }
- .that(topLayerRegion.region.width)
- .isEqual(bottomLayerRegion.region.width)
+ .that(topLayerRegion.region.bounds.width())
+ .isEqual(bottomLayerRegion.region.bounds.width())
topLayerRegion.notOverlaps(bottomLayerRegion.region)
// Layers of two activities sum to be fullscreen size on display.
topLayerRegion.plus(bottomLayerRegion.region).coversExactly(startDisplayBounds)
@@ -148,7 +151,7 @@
companion object {
/** {@inheritDoc} */
- private var startDisplayBounds = Rect.EMPTY
+ private var startDisplayBounds = Rect()
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
index e511b72..5df8b572 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.activityembedding.open
+import android.graphics.Rect
import android.platform.test.annotations.Presubmit
-import android.tools.datatypes.Rect
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -132,7 +132,7 @@
companion object {
/** {@inheritDoc} */
- private var startDisplayBounds = Rect.EMPTY
+ private var startDisplayBounds = Rect()
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
index 4352177..78004cc 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.activityembedding.open
+import android.graphics.Rect
import android.platform.test.annotations.Presubmit
-import android.tools.datatypes.Rect
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -143,7 +143,7 @@
companion object {
/** {@inheritDoc} */
- private var startDisplayBounds = Rect.EMPTY
+ private var startDisplayBounds = Rect()
/**
* Creates the test configurations.
*
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
index 62cf6cd..cf4edd5 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.activityembedding.open
+import android.graphics.Rect
import android.platform.test.annotations.Presubmit
-import android.tools.datatypes.Rect
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -156,11 +156,11 @@
it.timestamp
)
check { "height" }
- .that(mainActivityRegion.region.height)
- .isEqual(secondaryActivityRegion.region.height)
+ .that(mainActivityRegion.region.bounds.height())
+ .isEqual(secondaryActivityRegion.region.bounds.height())
check { "width" }
- .that(mainActivityRegion.region.width)
- .isEqual(secondaryActivityRegion.region.width)
+ .that(mainActivityRegion.region.bounds.width())
+ .isEqual(secondaryActivityRegion.region.bounds.width())
mainActivityRegion
.plus(secondaryActivityRegion.region)
.coversExactly(startDisplayBounds)
@@ -192,11 +192,11 @@
// Compare dimensions of two splits, given we're using default split attributes,
// both activities take up the same visible size on the display.
check { "height" }
- .that(leftLayerRegion.region.height)
- .isEqual(rightLayerRegion.region.height)
+ .that(leftLayerRegion.region.bounds.height())
+ .isEqual(rightLayerRegion.region.bounds.height())
check { "width" }
- .that(leftLayerRegion.region.width)
- .isEqual(rightLayerRegion.region.width)
+ .that(leftLayerRegion.region.bounds.width())
+ .isEqual(rightLayerRegion.region.bounds.width())
leftLayerRegion.notOverlaps(rightLayerRegion.region)
// Layers of two activities sum to be fullscreen size on display.
leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds)
@@ -211,7 +211,7 @@
companion object {
/** {@inheritDoc} */
- private var startDisplayBounds = Rect.EMPTY
+ private var startDisplayBounds = Rect()
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
index aa8b4ce..bc3696b 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.activityembedding.pip
+import android.graphics.Rect
import android.platform.test.annotations.Presubmit
-import android.tools.datatypes.Rect
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -79,11 +79,11 @@
// Compare dimensions of two splits, given we're using default split attributes,
// both activities take up the same visible size on the display.
check { "height" }
- .that(leftLayerRegion.region.height)
- .isEqual(rightLayerRegion.region.height)
+ .that(leftLayerRegion.region.bounds.height())
+ .isEqual(rightLayerRegion.region.bounds.height())
check { "width" }
- .that(leftLayerRegion.region.width)
- .isEqual(rightLayerRegion.region.width)
+ .that(leftLayerRegion.region.bounds.width())
+ .isEqual(rightLayerRegion.region.bounds.width())
leftLayerRegion.notOverlaps(rightLayerRegion.region)
leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds)
}
@@ -136,9 +136,11 @@
val pipWindowRegion =
visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
check { "height" }
- .that(pipWindowRegion.region.height)
- .isLower(startDisplayBounds.height / 2)
- check { "width" }.that(pipWindowRegion.region.width).isLower(startDisplayBounds.width)
+ .that(pipWindowRegion.region.bounds.height())
+ .isLower(startDisplayBounds.height() / 2)
+ check { "width" }
+ .that(pipWindowRegion.region.bounds.width())
+ .isLower(startDisplayBounds.width())
}
}
@@ -151,7 +153,7 @@
ComponentNameMatcher.PIP_CONTENT_OVERLAY.layerMatchesAnyOf(it) && it.isVisible
}
pipLayerList.zipWithNext { previous, current ->
- if (startDisplayBounds.width > startDisplayBounds.height) {
+ if (startDisplayBounds.width() > startDisplayBounds.height()) {
// Only verify when the display is landscape, because otherwise the final pip
// window can be to the left of the original secondary activity.
current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3)
@@ -162,8 +164,12 @@
}
flicker.assertLayersEnd {
val pipRegion = visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- check { "height" }.that(pipRegion.region.height).isLower(startDisplayBounds.height / 2)
- check { "width" }.that(pipRegion.region.width).isLower(startDisplayBounds.width)
+ check { "height" }
+ .that(pipRegion.region.bounds.height())
+ .isLower(startDisplayBounds.height() / 2)
+ check { "width" }
+ .that(pipRegion.region.bounds.width())
+ .isLower(startDisplayBounds.width())
}
}
@@ -175,7 +181,7 @@
invoke("secondaryLayerNotJumpToLeft") {
val secondaryVisibleRegion =
it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- if (secondaryVisibleRegion.region.isNotEmpty) {
+ if (!secondaryVisibleRegion.region.isEmpty) {
check { "left" }.that(secondaryVisibleRegion.region.bounds.left).isGreater(0)
}
}
@@ -222,7 +228,7 @@
companion object {
/** {@inheritDoc} */
- private var startDisplayBounds = Rect.EMPTY
+ private var startDisplayBounds = Rect()
/**
* Creates the test configurations.
*
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
index 3d834c1..f5e6c78 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
@@ -85,11 +85,11 @@
// Compare dimensions of two splits, given we're using default split attributes,
// both activities take up the same visible size on the display.
check { "height" }
- .that(leftLayerRegion.region.height)
- .isEqual(rightLayerRegion.region.height)
+ .that(leftLayerRegion.region.bounds.height())
+ .isEqual(rightLayerRegion.region.bounds.height())
check { "width" }
- .that(leftLayerRegion.region.width)
- .isEqual(rightLayerRegion.region.width)
+ .that(leftLayerRegion.region.bounds.width())
+ .isEqual(rightLayerRegion.region.bounds.width())
leftLayerRegion.notOverlaps(rightLayerRegion.region)
// Layers of two activities sum to be fullscreen size on display.
leftLayerRegion.plus(rightLayerRegion.region).coversExactly(display.layerStackSpace)
@@ -108,11 +108,11 @@
val rightLayerRegion =
this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
check { "height" }
- .that(leftLayerRegion.region.height)
- .isEqual(rightLayerRegion.region.height)
+ .that(leftLayerRegion.region.bounds.height())
+ .isEqual(rightLayerRegion.region.bounds.height())
check { "width" }
- .that(leftLayerRegion.region.width)
- .isEqual(rightLayerRegion.region.width)
+ .that(leftLayerRegion.region.bounds.width())
+ .isEqual(rightLayerRegion.region.bounds.width())
leftLayerRegion.notOverlaps(rightLayerRegion.region)
leftLayerRegion.plus(rightLayerRegion.region).coversExactly(display.layerStackSpace)
}
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
index 1390218..ee2c05e 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
@@ -16,9 +16,9 @@
package com.android.server.wm.flicker.activityembedding.rotation
+import android.graphics.Rect
import android.platform.test.annotations.Presubmit
import android.tools.Position
-import android.tools.datatypes.Rect
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.traces.Condition
@@ -97,7 +97,7 @@
val navBarPosition = display.navBarPosition(isGesturalNavigation)
val navBarRegion = dump.layerState
.getLayerWithBuffer(ComponentNameMatcher.NAV_BAR)
- ?.visibleRegion?.bounds ?: Rect.EMPTY
+ ?.visibleRegion?.bounds ?: Rect()
when (navBarPosition) {
Position.TOP ->
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index 7298e5f..fb92583 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -16,9 +16,9 @@
package com.android.server.wm.flicker.activityembedding.splitscreen
+import android.graphics.Rect
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
-import android.tools.datatypes.Rect
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -32,6 +32,7 @@
import com.android.wm.shell.flicker.utils.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.utils.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.utils.splitScreenDividerBecomesVisible
+import kotlin.math.abs
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
@@ -135,19 +136,25 @@
.plus(systemDivider.region)
.coversExactly(startDisplayBounds)
check { "ActivityEmbeddingSplitHeight" }
- .that(leftAELayerRegion.region.height)
- .isEqual(rightAELayerRegion.region.height)
+ .that(leftAELayerRegion.region.bounds.height())
+ .isEqual(rightAELayerRegion.region.bounds.height())
check { "SystemSplitHeight" }
- .that(rightAELayerRegion.region.height)
- .isEqual(secondaryAppLayerRegion.region.height)
+ .that(rightAELayerRegion.region.bounds.height())
+ .isEqual(secondaryAppLayerRegion.region.bounds.height())
// TODO(b/292283182): Remove this special case handling.
check { "ActivityEmbeddingSplitWidth" }
- .that(Math.abs(leftAELayerRegion.region.width - rightAELayerRegion.region.width))
+ .that(
+ abs(
+ leftAELayerRegion.region.bounds.width() -
+ rightAELayerRegion.region.bounds.width()
+ )
+ )
.isLower(2)
check { "SystemSplitWidth" }
.that(
- Math.abs(
- secondaryAppLayerRegion.region.width - 2 * rightAELayerRegion.region.width
+ abs(
+ secondaryAppLayerRegion.region.bounds.width() -
+ 2 * rightAELayerRegion.region.bounds.width()
)
)
.isLower(2)
@@ -167,18 +174,24 @@
val secondaryAppLayerRegion =
visibleRegion(ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
check { "ActivityEmbeddingSplitHeight" }
- .that(leftAEWindowRegion.region.height)
- .isEqual(rightAEWindowRegion.region.height)
+ .that(leftAEWindowRegion.region.bounds.height())
+ .isEqual(rightAEWindowRegion.region.bounds.height())
check { "SystemSplitHeight" }
- .that(rightAEWindowRegion.region.height)
- .isEqual(secondaryAppLayerRegion.region.height)
+ .that(rightAEWindowRegion.region.bounds.height())
+ .isEqual(secondaryAppLayerRegion.region.bounds.height())
check { "ActivityEmbeddingSplitWidth" }
- .that(Math.abs(leftAEWindowRegion.region.width - rightAEWindowRegion.region.width))
+ .that(
+ abs(
+ leftAEWindowRegion.region.bounds.width() -
+ rightAEWindowRegion.region.bounds.width()
+ )
+ )
.isLower(2)
check { "SystemSplitWidth" }
.that(
- Math.abs(
- secondaryAppLayerRegion.region.width - 2 * rightAEWindowRegion.region.width
+ abs(
+ secondaryAppLayerRegion.region.bounds.width() -
+ 2 * rightAEWindowRegion.region.bounds.width()
)
)
.isLower(2)
@@ -190,7 +203,7 @@
companion object {
/** {@inheritDoc} */
- private var startDisplayBounds = Rect.EMPTY
+ private var startDisplayBounds = Rect()
/**
* Creates the test configurations.
*
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index b1d78cb..a71599d 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -19,8 +19,9 @@
import android.app.Instrumentation
import android.app.WallpaperManager
import android.content.res.Resources
+import android.graphics.Rect
+import android.graphics.Region
import android.platform.test.annotations.Presubmit
-import android.tools.datatypes.Region
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -213,6 +214,12 @@
private fun LayersTraceSubject.visibleRegionCovers(
component: IComponentMatcher,
+ expectedArea: Rect,
+ isOptional: Boolean = true
+ ): LayersTraceSubject = visibleRegionCovers(component, Region(expectedArea), isOptional)
+
+ private fun LayersTraceSubject.visibleRegionCovers(
+ component: IComponentMatcher,
expectedArea: Region,
isOptional: Boolean = true
): LayersTraceSubject =
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
index 7e486ab..da8368f 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -83,11 +83,12 @@
}
if (imeSnapshotLayers.isNotEmpty()) {
val visibleAreas =
- imeSnapshotLayers
- .mapNotNull { imeSnapshotLayer -> imeSnapshotLayer.layer.visibleRegion }
+ imeSnapshotLayers.mapNotNull { imeSnapshotLayer ->
+ imeSnapshotLayer.layer.visibleRegion
+ }
val imeVisibleRegion = RegionSubject(visibleAreas, timestamp)
val appVisibleRegion = it.visibleRegion(imeTestApp)
- if (imeVisibleRegion.region.isNotEmpty) {
+ if (!imeVisibleRegion.region.isEmpty) {
imeVisibleRegion.coversAtMost(appVisibleRegion.region)
}
}
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index e8249bc..48ec4d1 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -115,7 +115,10 @@
.isEqual(true)
imeLayerSubjects.forEach { imeLayerSubject ->
- imeLayerSubject.check { "alpha" }.that(imeLayerSubject.layer.color.a).isEqual(1.0f)
+ imeLayerSubject
+ .check { "alpha" }
+ .that(imeLayerSubject.layer.color.alpha())
+ .isEqual(1.0f)
}
}
}
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
index 617237d..063088d 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
@@ -50,7 +50,10 @@
testApp.launchViaIntent(wmHelper)
testApp.openIME(wmHelper)
this.setRotation(flicker.scenario.startRotation)
- device.pressRecentApps()
+ if (flicker.scenario.isTablet) {
+ tapl.launchedAppState.swipeUpToUnstashTaskbar()
+ }
+ tapl.launchedAppState.switchToOverview()
wmHelper.StateSyncBuilder().withRecentsActivityVisible().waitForAndVerify()
}
transitions {
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index a14dc62..7aa525f 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -191,7 +191,7 @@
this.invoke("imeLayerIsVisibleAndAlignAppWidow") {
val imeVisibleRegion = it.visibleRegion(ComponentNameMatcher.IME)
val appVisibleRegion = it.visibleRegion(imeTestApp)
- if (imeVisibleRegion.region.isNotEmpty) {
+ if (!imeVisibleRegion.region.isEmpty) {
it.isVisible(ComponentNameMatcher.IME)
imeVisibleRegion.coversAtMost(appVisibleRegion.region)
}
diff --git a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 8b09b59..9bb62e1 100644
--- a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -16,9 +16,9 @@
package com.android.server.wm.flicker.quickswitch
+import android.graphics.Rect
import android.platform.test.annotations.Presubmit
import android.tools.NavBar
-import android.tools.datatypes.Rect
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -237,7 +237,7 @@
override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
companion object {
- private var startDisplayBounds = Rect.EMPTY
+ private var startDisplayBounds = Rect()
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index c54ddcf..491b994 100644
--- a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.quickswitch
+import android.graphics.Rect
import android.tools.NavBar
-import android.tools.datatypes.Rect
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -285,7 +285,7 @@
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
- private var startDisplayBounds = Rect.EMPTY
+ private var startDisplayBounds = Rect()
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index 69a84a0..de54c95 100644
--- a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -16,10 +16,10 @@
package com.android.server.wm.flicker.quickswitch
+import android.graphics.Rect
import android.platform.test.annotations.Presubmit
import android.tools.NavBar
import android.tools.Rotation
-import android.tools.datatypes.Rect
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -266,7 +266,7 @@
companion object {
/** {@inheritDoc} */
- private var startDisplayBounds = Rect.EMPTY
+ private var startDisplayBounds = Rect()
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 8853c1d..348d0af 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -279,12 +279,11 @@
subject.isVisible
}
val visibleAreas =
- snapshotLayers
- .mapNotNull { snapshotLayer -> snapshotLayer.layer.visibleRegion }
+ snapshotLayers.mapNotNull { snapshotLayer -> snapshotLayer.layer.visibleRegion }
val snapshotRegion = RegionSubject(visibleAreas, it.timestamp)
val appVisibleRegion = it.visibleRegion(component)
// Verify the size of snapshotRegion covers appVisibleRegion exactly in animation.
- if (snapshotRegion.region.isNotEmpty && appVisibleRegion.region.isNotEmpty) {
+ if (!snapshotRegion.region.isEmpty && !appVisibleRegion.region.isEmpty) {
snapshotRegion.coversExactly(appVisibleRegion.region)
}
}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
index ffed408..ef8d84f 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
@@ -45,13 +45,7 @@
require(gameView != null) { "Mock game app view not found." }
val bound = gameView.getVisibleBounds()
- return uiDevice.swipe(
- bound.centerX(),
- 0,
- bound.centerX(),
- bound.centerY(),
- SWIPE_STEPS
- )
+ return uiDevice.swipe(bound.centerX(), 0, bound.centerX(), bound.centerY(), SWIPE_STEPS)
}
/**
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
index b09e53b..634b6ee 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
@@ -17,8 +17,8 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.datatypes.Rect
-import android.tools.datatypes.Region
+import android.graphics.Rect
+import android.graphics.Region
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.helpers.FIND_TIMEOUT
import android.tools.helpers.SYSTEMUI_PACKAGE
@@ -86,7 +86,7 @@
.add("letterboxAppRepositioned") {
val letterboxAppWindow = getWindowRegion(wmHelper)
val appRegionBounds = letterboxAppWindow.bounds
- val appWidth = appRegionBounds.width
+ val appWidth = appRegionBounds.width()
return@add if (right)
appRegionBounds.left == displayBounds.right - appWidth &&
appRegionBounds.right == displayBounds.right
@@ -108,7 +108,7 @@
.add("letterboxAppRepositioned") {
val letterboxAppWindow = getWindowRegion(wmHelper)
val appRegionBounds = letterboxAppWindow.bounds
- val appHeight = appRegionBounds.height
+ val appHeight = appRegionBounds.height()
return@add if (bottom)
appRegionBounds.bottom == displayBounds.bottom &&
appRegionBounds.top == (displayBounds.bottom - appHeight + navBarHeight)
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index db933b3..43fd57b 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -18,10 +18,11 @@
import android.app.Instrumentation
import android.content.Intent
+import android.graphics.Rect
+import android.graphics.Region
import android.media.session.MediaController
import android.media.session.MediaSessionManager
-import android.tools.datatypes.Rect
-import android.tools.datatypes.Region
+import android.tools.datatypes.coversMoreThan
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.helpers.FIND_TIMEOUT
import android.tools.helpers.SYSTEMUI_PACKAGE
@@ -62,7 +63,7 @@
/** Drags the PIP window to the provided final coordinates without releasing the pointer. */
fun dragPipWindowAwayFromEdgeWithoutRelease(wmHelper: WindowManagerStateHelper, steps: Int) {
- val initWindowRect = getWindowRect(wmHelper).clone()
+ val initWindowRect = Rect(getWindowRect(wmHelper))
// initial pointer at the center of the window
val initialCoord =
@@ -101,7 +102,7 @@
* @throws IllegalStateException if default display bounds are not available
*/
fun dragPipWindowAwayFromEdge(wmHelper: WindowManagerStateHelper, steps: Int) {
- val initWindowRect = getWindowRect(wmHelper).clone()
+ val initWindowRect = Rect(getWindowRect(wmHelper))
// initial pointer at the center of the window
val startX = initWindowRect.centerX()
@@ -153,12 +154,12 @@
val windowRect = getWindowRect(wmHelper)
// first pointer's initial x coordinate is halfway between the left edge and the center
- val initLeftX = (windowRect.centerX() - windowRect.width / 4).toFloat()
+ val initLeftX = (windowRect.centerX() - windowRect.width() / 4).toFloat()
// second pointer's initial x coordinate is halfway between the right edge and the center
- val initRightX = (windowRect.centerX() + windowRect.width / 4).toFloat()
+ val initRightX = (windowRect.centerX() + windowRect.width() / 4).toFloat()
// horizontal distance the window should increase by
- val distIncrease = windowRect.width * percent
+ val distIncrease = windowRect.width() * percent
// final x-coordinates
val finalLeftX = initLeftX - (distIncrease / 2)
@@ -183,7 +184,7 @@
adjustedSteps
)
- waitForPipWindowToExpandFrom(wmHelper, Region.from(windowRect))
+ waitForPipWindowToExpandFrom(wmHelper, Region(windowRect))
}
/**
@@ -201,12 +202,12 @@
val windowRect = getWindowRect(wmHelper)
// first pointer's initial x coordinate is halfway between the left edge and the center
- val initLeftX = (windowRect.centerX() - windowRect.width / 4).toFloat()
+ val initLeftX = (windowRect.centerX() - windowRect.width() / 4).toFloat()
// second pointer's initial x coordinate is halfway between the right edge and the center
- val initRightX = (windowRect.centerX() + windowRect.width / 4).toFloat()
+ val initRightX = (windowRect.centerX() + windowRect.width() / 4).toFloat()
// decrease by the distance specified through the percentage
- val distDecrease = windowRect.width * percent
+ val distDecrease = windowRect.width() * percent
// get the final x-coordinates and make sure they are not passing the center of the window
val finalLeftX = Math.min(initLeftX + (distDecrease / 2), windowRect.centerX().toFloat())
@@ -231,7 +232,7 @@
adjustedSteps
)
- waitForPipWindowToMinimizeFrom(wmHelper, Region.from(windowRect))
+ waitForPipWindowToMinimizeFrom(wmHelper, Region(windowRect))
}
/**
@@ -375,7 +376,7 @@
uiDevice.click(windowRect.centerX(), windowRect.centerY())
Log.d(TAG, "Wait for app transition to end")
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
- waitForPipWindowToExpandFrom(wmHelper, Region.from(windowRect))
+ waitForPipWindowToExpandFrom(wmHelper, Region(windowRect))
}
private fun waitForPipWindowToExpandFrom(
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index f6f766a..8d2b927 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -19,17 +19,26 @@
import android.content.Context
import android.content.ContextWrapper
+import android.hardware.display.DisplayManager
import android.hardware.display.DisplayViewport
+import android.hardware.display.VirtualDisplay
import android.hardware.input.InputManager
import android.hardware.input.InputManagerGlobal
+import android.os.InputEventInjectionSync
+import android.os.SystemClock
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.Settings
-import android.test.mock.MockContentResolver
+import android.view.View.OnKeyListener
import android.view.Display
+import android.view.InputDevice
+import android.view.KeyEvent
import android.view.PointerIcon
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+import android.test.mock.MockContentResolver
import androidx.test.platform.app.InstrumentationRegistry
import com.android.internal.util.test.FakeSettingsProvider
import com.google.common.truth.Truth.assertThat
@@ -48,6 +57,7 @@
import org.mockito.Mockito.`when`
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
@@ -412,6 +422,174 @@
verify(wmCallbacks).notifyPointerDisplayIdChanged(overrideDisplayId, 0f, 0f)
thread.join(100 /*millis*/)
}
+
+ private fun createVirtualDisplays(count: Int): List<VirtualDisplay> {
+ val displayManager: DisplayManager = context.getSystemService(
+ DisplayManager::class.java
+ ) as DisplayManager
+ val virtualDisplays = mutableListOf<VirtualDisplay>()
+ for (i in 0 until count) {
+ virtualDisplays.add(displayManager.createVirtualDisplay(
+ /* displayName= */ "testVirtualDisplay$i",
+ /* width= */ 100,
+ /* height= */ 100,
+ /* densityDpi= */ 100,
+ /* surface= */ null,
+ /* flags= */ 0
+ ))
+ }
+ return virtualDisplays
+ }
+
+ // Helper function that creates a KeyEvent with Keycode A with the given action
+ private fun createKeycodeAEvent(inputDevice: InputDevice, action: Int): KeyEvent {
+ val eventTime = SystemClock.uptimeMillis()
+ return KeyEvent(
+ /* downTime= */ eventTime,
+ /* eventTime= */ eventTime,
+ /* action= */ action,
+ /* code= */ KeyEvent.KEYCODE_A,
+ /* repeat= */ 0,
+ /* metaState= */ 0,
+ /* deviceId= */ inputDevice.id,
+ /* scanCode= */ 0,
+ /* flags= */ KeyEvent.FLAG_FROM_SYSTEM,
+ /* source= */ InputDevice.SOURCE_KEYBOARD
+ )
+ }
+
+ private fun createInputDevice(): InputDevice {
+ return InputDevice.Builder()
+ .setId(123)
+ .setName("abc")
+ .setDescriptor("def")
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .build()
+ }
+
+ @Test
+ fun addUniqueIdAssociationByDescriptor_verifyAssociations() {
+ // Overall goal is to have 2 displays and verify that events from the InputDevice are
+ // sent only to the view that is on the associated display.
+ // So, associate the InputDevice with display 1, then send and verify KeyEvents.
+ // Then remove associations, then associate the InputDevice with display 2, then send
+ // and verify commands.
+
+ // Make 2 virtual displays with some mock SurfaceViews
+ val mockSurfaceView1 = mock(SurfaceView::class.java)
+ val mockSurfaceView2 = mock(SurfaceView::class.java)
+ val mockSurfaceHolder1 = mock(SurfaceHolder::class.java)
+ `when`(mockSurfaceView1.holder).thenReturn(mockSurfaceHolder1)
+ val mockSurfaceHolder2 = mock(SurfaceHolder::class.java)
+ `when`(mockSurfaceView2.holder).thenReturn(mockSurfaceHolder2)
+
+ val virtualDisplays = createVirtualDisplays(2)
+
+ // Simulate an InputDevice
+ val inputDevice = createInputDevice()
+
+ // Associate input device with display
+ service.addUniqueIdAssociationByDescriptor(
+ inputDevice.descriptor,
+ virtualDisplays[0].display.displayId.toString()
+ )
+
+ // Simulate 2 different KeyEvents
+ val downEvent = createKeycodeAEvent(inputDevice, KeyEvent.ACTION_DOWN)
+ val upEvent = createKeycodeAEvent(inputDevice, KeyEvent.ACTION_UP)
+
+ // Create a mock OnKeyListener object
+ val mockOnKeyListener = mock(OnKeyListener::class.java)
+
+ // Verify that the event went to Display 1 not Display 2
+ service.injectInputEvent(downEvent, InputEventInjectionSync.NONE)
+
+ // Call the onKey method on the mock OnKeyListener object
+ mockOnKeyListener.onKey(mockSurfaceView1, /* keyCode= */ KeyEvent.KEYCODE_A, downEvent)
+ mockOnKeyListener.onKey(mockSurfaceView2, /* keyCode= */ KeyEvent.KEYCODE_A, upEvent)
+
+ // Verify that the onKey method was called with the expected arguments
+ verify(mockOnKeyListener).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, downEvent)
+ verify(mockOnKeyListener, never()).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, downEvent)
+
+ // Remove association
+ service.removeUniqueIdAssociationByDescriptor(inputDevice.descriptor)
+
+ // Associate with Display 2
+ service.addUniqueIdAssociationByDescriptor(
+ inputDevice.descriptor,
+ virtualDisplays[1].display.displayId.toString()
+ )
+
+ // Simulate a KeyEvent
+ service.injectInputEvent(upEvent, InputEventInjectionSync.NONE)
+
+ // Verify that the event went to Display 2 not Display 1
+ verify(mockOnKeyListener).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, upEvent)
+ verify(mockOnKeyListener, never()).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, upEvent)
+ }
+
+ @Test
+ fun addUniqueIdAssociationByPort_verifyAssociations() {
+ // Overall goal is to have 2 displays and verify that events from the InputDevice are
+ // sent only to the view that is on the associated display.
+ // So, associate the InputDevice with display 1, then send and verify KeyEvents.
+ // Then remove associations, then associate the InputDevice with display 2, then send
+ // and verify commands.
+
+ // Make 2 virtual displays with some mock SurfaceViews
+ val mockSurfaceView1 = mock(SurfaceView::class.java)
+ val mockSurfaceView2 = mock(SurfaceView::class.java)
+ val mockSurfaceHolder1 = mock(SurfaceHolder::class.java)
+ `when`(mockSurfaceView1.holder).thenReturn(mockSurfaceHolder1)
+ val mockSurfaceHolder2 = mock(SurfaceHolder::class.java)
+ `when`(mockSurfaceView2.holder).thenReturn(mockSurfaceHolder2)
+
+ val virtualDisplays = createVirtualDisplays(2)
+
+ // Simulate an InputDevice
+ val inputDevice = createInputDevice()
+
+ // Associate input device with display
+ service.addUniqueIdAssociationByPort(
+ inputDevice.name,
+ virtualDisplays[0].display.displayId.toString()
+ )
+
+ // Simulate 2 different KeyEvents
+ val downEvent = createKeycodeAEvent(inputDevice, KeyEvent.ACTION_DOWN)
+ val upEvent = createKeycodeAEvent(inputDevice, KeyEvent.ACTION_UP)
+
+ // Create a mock OnKeyListener object
+ val mockOnKeyListener = mock(OnKeyListener::class.java)
+
+ // Verify that the event went to Display 1 not Display 2
+ service.injectInputEvent(downEvent, InputEventInjectionSync.NONE)
+
+ // Call the onKey method on the mock OnKeyListener object
+ mockOnKeyListener.onKey(mockSurfaceView1, /* keyCode= */ KeyEvent.KEYCODE_A, downEvent)
+ mockOnKeyListener.onKey(mockSurfaceView2, /* keyCode= */ KeyEvent.KEYCODE_A, upEvent)
+
+ // Verify that the onKey method was called with the expected arguments
+ verify(mockOnKeyListener).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, downEvent)
+ verify(mockOnKeyListener, never()).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, downEvent)
+
+ // Remove association
+ service.removeUniqueIdAssociationByPort(inputDevice.name)
+
+ // Associate with Display 2
+ service.addUniqueIdAssociationByPort(
+ inputDevice.name,
+ virtualDisplays[1].display.displayId.toString()
+ )
+
+ // Simulate a KeyEvent
+ service.injectInputEvent(upEvent, InputEventInjectionSync.NONE)
+
+ // Verify that the event went to Display 2 not Display 1
+ verify(mockOnKeyListener).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, upEvent)
+ verify(mockOnKeyListener, never()).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, upEvent)
+ }
}
private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall)
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index 80282c3..93f97cb 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -523,6 +523,12 @@
createImeSubtypeForLanguageTagAndLayoutType("en-Deva-US", "")
)
)
+ // If prefer layout with empty country over mismatched country
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("en-AU", "qwerty"),
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
}
@Test
diff --git a/tests/OneMedia/Android.bp b/tests/OneMedia/Android.bp
index 5c73177..a43cd39 100644
--- a/tests/OneMedia/Android.bp
+++ b/tests/OneMedia/Android.bp
@@ -16,6 +16,7 @@
platform_apis: true,
certificate: "platform",
libs: ["org.apache.http.legacy"],
+ optional_uses_libs: ["org.apache.http.legacy"],
optimize: {
enabled: false,
},
diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java
index 7558332..f88d82b 100644
--- a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java
+++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java
@@ -85,6 +85,8 @@
assertTrue(TelephonyUtils.isValidPlmn("45006"));
assertFalse(TelephonyUtils.isValidPlmn("1234567"));
assertFalse(TelephonyUtils.isValidPlmn("1234"));
+ assertFalse(TelephonyUtils.isValidPlmn(""));
+ assertFalse(TelephonyUtils.isValidPlmn(null));
}
@Test
@@ -94,6 +96,19 @@
assertFalse(TelephonyUtils.isValidService(FIRST_SERVICE_TYPE - 1));
assertFalse(TelephonyUtils.isValidService(LAST_SERVICE_TYPE + 1));
}
+
+ @Test
+ public void testIsValidCountryCode() {
+ assertTrue(TelephonyUtils.isValidCountryCode("US"));
+ assertTrue(TelephonyUtils.isValidCountryCode("cn"));
+ assertFalse(TelephonyUtils.isValidCountryCode("11"));
+ assertFalse(TelephonyUtils.isValidCountryCode("USA"));
+ assertFalse(TelephonyUtils.isValidCountryCode("chn"));
+ assertFalse(TelephonyUtils.isValidCountryCode("U"));
+ assertFalse(TelephonyUtils.isValidCountryCode("G7"));
+ assertFalse(TelephonyUtils.isValidCountryCode(""));
+ assertFalse(TelephonyUtils.isValidCountryCode(null));
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
index fdf8fb8..c8b60e5 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
@@ -17,9 +17,11 @@
package com.android.server.vcn.routeselection;
import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY;
+import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY;
import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY;
-import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.PACKET_LOSS_UNAVALAIBLE;
+import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.MIN_VALID_EXPECTED_RX_PACKET_NUM;
+import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.getMaxSeqNumIncreasePerSecond;
import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertEquals;
@@ -44,6 +46,7 @@
import android.os.OutcomeReceiver;
import android.os.PowerManager;
+import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculationResult;
import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculator;
import com.android.server.vcn.routeselection.NetworkMetricMonitor.IpSecTransformWrapper;
import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback;
@@ -65,6 +68,7 @@
private static final int REPLAY_BITMAP_LEN_BYTE = 512;
private static final int REPLAY_BITMAP_LEN_BIT = REPLAY_BITMAP_LEN_BYTE * 8;
private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD = 5;
+ private static final int MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED = -1;
private static final long POLL_IPSEC_STATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(30L);
@Mock private IpSecTransformWrapper mIpSecTransform;
@@ -91,6 +95,9 @@
eq(VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY),
anyInt()))
.thenReturn(IPSEC_PACKET_LOSS_PERCENT_THRESHOLD);
+ when(mCarrierConfig.getInt(
+ eq(VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY), anyInt()))
+ .thenReturn(MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED);
when(mDependencies.getPacketLossCalculator()).thenReturn(mPacketLossCalculator);
@@ -112,6 +119,20 @@
.build();
}
+ private static IpSecTransformState newNextTransformState(
+ IpSecTransformState before,
+ long timeDiffMillis,
+ long rxSeqNoDiff,
+ long packtCountDiff,
+ int packetInWin) {
+ return new IpSecTransformState.Builder()
+ .setTimestampMillis(before.getTimestampMillis() + timeDiffMillis)
+ .setRxHighestSequenceNumber(before.getRxHighestSequenceNumber() + rxSeqNoDiff)
+ .setPacketCount(before.getPacketCount() + packtCountDiff)
+ .setReplayBitmap(newReplayBitmap(packetInWin))
+ .build();
+ }
+
private static byte[] newReplayBitmap(int receivedPktCnt) {
final BitSet bitSet = new BitSet(REPLAY_BITMAP_LEN_BIT);
for (int i = 0; i < receivedPktCnt; i++) {
@@ -165,7 +186,7 @@
// Verify the first polled state is stored
assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState());
verify(mPacketLossCalculator, never())
- .getPacketLossRatePercentage(any(), any(), anyString());
+ .getPacketLossRatePercentage(any(), any(), anyInt(), anyString());
// Verify next poll is scheduled
assertNull(mTestLooper.nextMessage());
@@ -278,7 +299,7 @@
xfrmStateReceiver.onResult(newTransformState(1, 1, newReplayBitmap(1)));
verify(mPacketLossCalculator, never())
- .getPacketLossRatePercentage(any(), any(), anyString());
+ .getPacketLossRatePercentage(any(), any(), anyInt(), anyString());
}
@Test
@@ -289,17 +310,19 @@
xfrmStateReceiver.onError(new RuntimeException("Test"));
verify(mPacketLossCalculator, never())
- .getPacketLossRatePercentage(any(), any(), anyString());
+ .getPacketLossRatePercentage(any(), any(), anyInt(), anyString());
}
private void checkHandleLossRate(
- int mockPacketLossRate, boolean isLastStateExpectedToUpdate, boolean isCallbackExpected)
+ PacketLossCalculationResult mockPacketLossRate,
+ boolean isLastStateExpectedToUpdate,
+ boolean isCallbackExpected)
throws Exception {
final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver =
startMonitorAndCaptureStateReceiver();
doReturn(mockPacketLossRate)
.when(mPacketLossCalculator)
- .getPacketLossRatePercentage(any(), any(), anyString());
+ .getPacketLossRatePercentage(any(), any(), anyInt(), anyString());
// Mock receiving two states with mTransformStateInitial and an arbitrary transformNew
final IpSecTransformState transformNew = newTransformState(1, 1, newReplayBitmap(1));
@@ -309,7 +332,10 @@
// Verifications
verify(mPacketLossCalculator)
.getPacketLossRatePercentage(
- eq(mTransformStateInitial), eq(transformNew), anyString());
+ eq(mTransformStateInitial),
+ eq(transformNew),
+ eq(MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED),
+ anyString());
if (isLastStateExpectedToUpdate) {
assertEquals(transformNew, mIpSecPacketLossDetector.getLastTransformState());
@@ -327,30 +353,53 @@
@Test
public void testHandleLossRate_validationPass() throws Exception {
checkHandleLossRate(
- 2, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */);
+ PacketLossCalculationResult.valid(2),
+ true /* isLastStateExpectedToUpdate */,
+ true /* isCallbackExpected */);
}
@Test
public void testHandleLossRate_validationFail() throws Exception {
checkHandleLossRate(
- 22, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */);
+ PacketLossCalculationResult.valid(22),
+ true /* isLastStateExpectedToUpdate */,
+ true /* isCallbackExpected */);
verify(mConnectivityManager).reportNetworkConnectivity(mNetwork, false);
}
@Test
public void testHandleLossRate_resultUnavalaible() throws Exception {
checkHandleLossRate(
- PACKET_LOSS_UNAVALAIBLE,
+ PacketLossCalculationResult.invalid(),
false /* isLastStateExpectedToUpdate */,
false /* isCallbackExpected */);
}
+ @Test
+ public void testHandleLossRate_unusualSeqNumLeap_highLossRate() throws Exception {
+ checkHandleLossRate(
+ PacketLossCalculationResult.unusualSeqNumLeap(22),
+ true /* isLastStateExpectedToUpdate */,
+ false /* isCallbackExpected */);
+ }
+
+ @Test
+ public void testHandleLossRate_unusualSeqNumLeap_lowLossRate() throws Exception {
+ checkHandleLossRate(
+ PacketLossCalculationResult.unusualSeqNumLeap(2),
+ true /* isLastStateExpectedToUpdate */,
+ true /* isCallbackExpected */);
+ }
+
private void checkGetPacketLossRate(
- IpSecTransformState oldState, IpSecTransformState newState, int expectedLossRate)
+ IpSecTransformState oldState,
+ IpSecTransformState newState,
+ PacketLossCalculationResult expectedLossRate)
throws Exception {
assertEquals(
expectedLossRate,
- mPacketLossCalculator.getPacketLossRatePercentage(oldState, newState, TAG));
+ mPacketLossCalculator.getPacketLossRatePercentage(
+ oldState, newState, MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED, TAG));
}
private void checkGetPacketLossRate(
@@ -362,14 +411,45 @@
throws Exception {
final IpSecTransformState newState =
newTransformState(rxSeqNo, packetCount, newReplayBitmap(packetInWin));
+ checkGetPacketLossRate(
+ oldState, newState, PacketLossCalculationResult.valid(expectedDataLossRate));
+ }
+
+ private void checkGetPacketLossRate(
+ IpSecTransformState oldState,
+ int rxSeqNo,
+ int packetCount,
+ int packetInWin,
+ PacketLossCalculationResult expectedDataLossRate)
+ throws Exception {
+ final IpSecTransformState newState =
+ newTransformState(rxSeqNo, packetCount, newReplayBitmap(packetInWin));
checkGetPacketLossRate(oldState, newState, expectedDataLossRate);
}
@Test
public void testGetPacketLossRate_replayWindowUnchanged() throws Exception {
checkGetPacketLossRate(
- mTransformStateInitial, mTransformStateInitial, PACKET_LOSS_UNAVALAIBLE);
- checkGetPacketLossRate(mTransformStateInitial, 3000, 2000, 2000, PACKET_LOSS_UNAVALAIBLE);
+ mTransformStateInitial,
+ mTransformStateInitial,
+ PacketLossCalculationResult.invalid());
+ checkGetPacketLossRate(
+ mTransformStateInitial, 3000, 2000, 2000, PacketLossCalculationResult.invalid());
+ }
+
+ @Test
+ public void testGetPacketLossRate_expectedPacketNumTooFew() throws Exception {
+ final int oldRxNo = 4096;
+ final int oldPktCnt = 4096;
+ final int pktCntDiff = MIN_VALID_EXPECTED_RX_PACKET_NUM - 1;
+ final byte[] bitmapReceiveAll = newReplayBitmap(4096);
+
+ final IpSecTransformState oldState =
+ newTransformState(oldRxNo, oldPktCnt, bitmapReceiveAll);
+ final IpSecTransformState newState =
+ newTransformState(oldRxNo + pktCntDiff, oldPktCnt + pktCntDiff, bitmapReceiveAll);
+
+ checkGetPacketLossRate(oldState, newState, PacketLossCalculationResult.invalid());
}
@Test
@@ -419,6 +499,45 @@
checkGetPacketLossRate(oldState, 20000, 14000, 3000, 10);
}
+ private void checkGetPktLossRate_unusualSeqNumLeap(
+ int maxSeqNumIncreasePerSecond,
+ int timeDiffMillis,
+ int rxSeqNoDiff,
+ PacketLossCalculationResult expected)
+ throws Exception {
+ final IpSecTransformState oldState = mTransformStateInitial;
+ final IpSecTransformState newState =
+ newNextTransformState(
+ oldState,
+ timeDiffMillis,
+ rxSeqNoDiff,
+ 1 /* packtCountDiff */,
+ 1 /* packetInWin */);
+
+ assertEquals(
+ expected,
+ mPacketLossCalculator.getPacketLossRatePercentage(
+ oldState, newState, maxSeqNumIncreasePerSecond, TAG));
+ }
+
+ @Test
+ public void testGetPktLossRate_unusualSeqNumLeap() throws Exception {
+ checkGetPktLossRate_unusualSeqNumLeap(
+ 10000 /* maxSeqNumIncreasePerSecond */,
+ (int) TimeUnit.SECONDS.toMillis(2L),
+ 30000 /* rxSeqNoDiff */,
+ PacketLossCalculationResult.unusualSeqNumLeap(100));
+ }
+
+ @Test
+ public void testGetPktLossRate_unusualSeqNumLeap_smallSeqNumDiff() throws Exception {
+ checkGetPktLossRate_unusualSeqNumLeap(
+ 10000 /* maxSeqNumIncreasePerSecond */,
+ (int) TimeUnit.SECONDS.toMillis(2L),
+ 5000 /* rxSeqNoDiff */,
+ PacketLossCalculationResult.valid(100));
+ }
+
// Verify the polling event is scheduled with expected delays
private void verifyPollEventDelayAndScheduleNext(long expectedDelayMs) {
if (expectedDelayMs > 0) {
@@ -445,4 +564,24 @@
// Verify the 3rd poll is scheduled with configured delay
verifyPollEventDelayAndScheduleNext(POLL_IPSEC_STATE_INTERVAL_MS);
}
+
+ @Test
+ public void testGetMaxSeqNumIncreasePerSecond() throws Exception {
+ final int seqNumLeapNegative = 500_000;
+ when(mCarrierConfig.getInt(
+ eq(VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY), anyInt()))
+ .thenReturn(seqNumLeapNegative);
+ assertEquals(seqNumLeapNegative, getMaxSeqNumIncreasePerSecond(mCarrierConfig));
+ }
+
+ @Test
+ public void testGetMaxSeqNumIncreasePerSecond_negativeValue() throws Exception {
+ final int seqNumLeapNegative = -10;
+ when(mCarrierConfig.getInt(
+ eq(VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY), anyInt()))
+ .thenReturn(seqNumLeapNegative);
+ assertEquals(
+ MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED,
+ getMaxSeqNumIncreasePerSecond(mCarrierConfig));
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
index af6daa1..6189fb0 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
@@ -123,6 +123,7 @@
mSetFlagsRule.enableFlags(Flags.FLAG_VALIDATE_NETWORK_ON_IPSEC_LOSS);
mSetFlagsRule.enableFlags(Flags.FLAG_EVALUATE_IPSEC_LOSS_ON_LP_NC_CHANGE);
+ mSetFlagsRule.enableFlags(Flags.FLAG_HANDLE_SEQ_NUM_LEAP);
when(mNetwork.getNetId()).thenReturn(-1);
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslConverter.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslConverter.java
index c1c520e..191f38d 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslConverter.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslConverter.java
@@ -59,13 +59,16 @@
switch (format) {
case HUMAN_READABLE:
Element appMetadataBundles =
- XmlUtils.getSingleElement(document, XmlUtils.HR_TAG_APP_METADATA_BUNDLES);
+ XmlUtils.getSingleChildElement(
+ document, XmlUtils.HR_TAG_APP_METADATA_BUNDLES, true);
return new AndroidSafetyLabelFactory()
.createFromHrElements(XmlUtils.listOf(appMetadataBundles));
case ON_DEVICE:
- throw new IllegalArgumentException(
- "Parsing from on-device format is not supported at this time.");
+ Element bundleEle =
+ XmlUtils.getSingleChildElement(document, XmlUtils.OD_TAG_BUNDLE, true);
+ return new AndroidSafetyLabelFactory()
+ .createFromOdElements(XmlUtils.listOf(bundleEle));
default:
throw new IllegalStateException("Unrecognized input format.");
}
@@ -88,8 +91,10 @@
switch (format) {
case HUMAN_READABLE:
- throw new IllegalArgumentException(
- "Outputting human-readable format is not supported at this time.");
+ for (var child : asl.toHrDomElements(document)) {
+ document.appendChild(child);
+ }
+ break;
case ON_DEVICE:
for (var child : asl.toOdDomElements(document)) {
document.appendChild(child);
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabel.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabel.java
index 112b92c..72140a1 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabel.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabel.java
@@ -61,4 +61,21 @@
}
return XmlUtils.listOf(aslEle);
}
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element aslEle = doc.createElement(XmlUtils.HR_TAG_APP_METADATA_BUNDLES);
+ aslEle.setAttribute(XmlUtils.HR_ATTR_VERSION, String.valueOf(mVersion));
+ if (mSafetyLabels != null) {
+ XmlUtils.appendChildren(aslEle, mSafetyLabels.toHrDomElements(doc));
+ }
+ if (mSystemAppSafetyLabel != null) {
+ XmlUtils.appendChildren(aslEle, mSystemAppSafetyLabel.toHrDomElements(doc));
+ }
+ if (mTransparencyInfo != null) {
+ XmlUtils.appendChildren(aslEle, mTransparencyInfo.toHrDomElements(doc));
+ }
+ return XmlUtils.listOf(aslEle);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabelFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabelFactory.java
index b69c30f..c53cbbf 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabelFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AndroidSafetyLabelFactory.java
@@ -55,4 +55,33 @@
return new AndroidSafetyLabel(
version, systemAppSafetyLabel, safetyLabels, transparencyInfo);
}
+
+ /** Creates an {@link AndroidSafetyLabel} from on-device DOM elements */
+ @Override
+ public AndroidSafetyLabel createFromOdElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element bundleEle = XmlUtils.getSingleElement(elements);
+ Long version = XmlUtils.getOdLongEle(bundleEle, XmlUtils.OD_NAME_VERSION, true);
+
+ Element safetyLabelsEle =
+ XmlUtils.getOdPbundleWithName(bundleEle, XmlUtils.OD_NAME_SAFETY_LABELS, false);
+ SafetyLabels safetyLabels =
+ new SafetyLabelsFactory().createFromOdElements(XmlUtils.listOf(safetyLabelsEle));
+
+ Element systemAppSafetyLabelEle =
+ XmlUtils.getOdPbundleWithName(
+ bundleEle, XmlUtils.OD_NAME_SYSTEM_APP_SAFETY_LABEL, false);
+ SystemAppSafetyLabel systemAppSafetyLabel =
+ new SystemAppSafetyLabelFactory()
+ .createFromOdElements(XmlUtils.listOf(systemAppSafetyLabelEle));
+
+ Element transparencyInfoEle =
+ XmlUtils.getOdPbundleWithName(bundleEle, XmlUtils.OD_NAME_TRANSPARENCY_INFO, false);
+ TransparencyInfo transparencyInfo =
+ new TransparencyInfoFactory()
+ .createFromOdElements(XmlUtils.listOf(transparencyInfoEle));
+
+ return new AndroidSafetyLabel(
+ version, systemAppSafetyLabel, safetyLabels, transparencyInfo);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfo.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfo.java
index 3f1ddeb..129733e 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfo.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfo.java
@@ -142,4 +142,59 @@
}
return XmlUtils.listOf(appInfoEle);
}
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element appInfoEle = doc.createElement(XmlUtils.HR_TAG_APP_INFO);
+ if (this.mTitle != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_TITLE, this.mTitle);
+ }
+ if (this.mDescription != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_DESCRIPTION, this.mDescription);
+ }
+ if (this.mContainsAds != null) {
+ appInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_CONTAINS_ADS, String.valueOf(this.mContainsAds));
+ }
+ if (this.mObeyAps != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_OBEY_APS, String.valueOf(this.mObeyAps));
+ }
+ if (this.mAdsFingerprinting != null) {
+ appInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_ADS_FINGERPRINTING, String.valueOf(this.mAdsFingerprinting));
+ }
+ if (this.mSecurityFingerprinting != null) {
+ appInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_SECURITY_FINGERPRINTING,
+ String.valueOf(this.mSecurityFingerprinting));
+ }
+ if (this.mPrivacyPolicy != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_PRIVACY_POLICY, this.mPrivacyPolicy);
+ }
+ if (this.mSecurityEndpoints != null) {
+ appInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_SECURITY_ENDPOINTS, String.join("|", this.mSecurityEndpoints));
+ }
+ if (this.mFirstPartyEndpoints != null) {
+ appInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_FIRST_PARTY_ENDPOINTS,
+ String.join("|", this.mFirstPartyEndpoints));
+ }
+ if (this.mServiceProviderEndpoints != null) {
+ appInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_SERVICE_PROVIDER_ENDPOINTS,
+ String.join("|", this.mServiceProviderEndpoints));
+ }
+ if (this.mCategory != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_CATEGORY, this.mCategory);
+ }
+ if (this.mEmail != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_EMAIL, this.mEmail);
+ }
+ if (this.mWebsite != null) {
+ appInfoEle.setAttribute(XmlUtils.HR_ATTR_WEBSITE, this.mWebsite);
+ }
+ return XmlUtils.listOf(appInfoEle);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java
index 59a437d..c506961 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java
@@ -35,15 +35,16 @@
return null;
}
- String title = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_TITLE);
- String description = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_DESCRIPTION);
+ String title = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_TITLE, true);
+ String description = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_DESCRIPTION, true);
Boolean containsAds = XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_CONTAINS_ADS, true);
Boolean obeyAps = XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_OBEY_APS, true);
Boolean adsFingerprinting =
XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_ADS_FINGERPRINTING, true);
Boolean securityFingerprinting =
XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_SECURITY_FINGERPRINTING, true);
- String privacyPolicy = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_PRIVACY_POLICY);
+ String privacyPolicy =
+ XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_PRIVACY_POLICY, true);
List<String> securityEndpoints =
XmlUtils.getPipelineSplitAttr(
appInfoEle, XmlUtils.HR_ATTR_SECURITY_ENDPOINTS, true);
@@ -53,8 +54,8 @@
List<String> serviceProviderEndpoints =
XmlUtils.getPipelineSplitAttr(
appInfoEle, XmlUtils.HR_ATTR_SERVICE_PROVIDER_ENDPOINTS, true);
- String category = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_CATEGORY);
- String email = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_EMAIL);
+ String category = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_CATEGORY, true);
+ String email = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_EMAIL, true);
String website = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_WEBSITE, false);
return new AppInfo(
@@ -72,4 +73,52 @@
email,
website);
}
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public AppInfo createFromOdElements(List<Element> elements) throws MalformedXmlException {
+ Element appInfoEle = XmlUtils.getSingleElement(elements);
+ if (appInfoEle == null) {
+ AslgenUtil.logI("No AppInfo found in od format.");
+ return null;
+ }
+
+ String title = XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_TITLE, true);
+ String description =
+ XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_DESCRIPTION, true);
+ Boolean containsAds =
+ XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_CONTAINS_ADS, true);
+ Boolean obeyAps = XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_OBEY_APS, true);
+ Boolean adsFingerprinting =
+ XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_ADS_FINGERPRINTING, true);
+ Boolean securityFingerprinting =
+ XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_SECURITY_FINGERPRINTING, true);
+ String privacyPolicy =
+ XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_PRIVACY_POLICY, true);
+ List<String> securityEndpoints =
+ XmlUtils.getOdStringArray(appInfoEle, XmlUtils.OD_NAME_SECURITY_ENDPOINT, true);
+ List<String> firstPartyEndpoints =
+ XmlUtils.getOdStringArray(appInfoEle, XmlUtils.OD_NAME_FIRST_PARTY_ENDPOINT, true);
+ List<String> serviceProviderEndpoints =
+ XmlUtils.getOdStringArray(
+ appInfoEle, XmlUtils.OD_NAME_SERVICE_PROVIDER_ENDPOINT, true);
+ String category = XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_CATEGORY, true);
+ String email = XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_EMAIL, true);
+ String website = XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_WEBSITE, false);
+
+ return new AppInfo(
+ title,
+ description,
+ containsAds,
+ obeyAps,
+ adsFingerprinting,
+ securityFingerprinting,
+ privacyPolicy,
+ securityEndpoints,
+ firstPartyEndpoints,
+ serviceProviderEndpoints,
+ category,
+ email,
+ website);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallable.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallable.java
index 48747cc..0a70e7d0 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallable.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallable.java
@@ -23,6 +23,9 @@
public interface AslMarshallable {
- /** Creates the on-device DOM element from the AslMarshallable Java Object. */
+ /** Creates the on-device DOM elements from the AslMarshallable Java Object. */
List<Element> toOdDomElements(Document doc);
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ List<Element> toHrDomElements(Document doc);
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallableFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallableFactory.java
index a49b3e7..3958290 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallableFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AslMarshallableFactory.java
@@ -24,6 +24,9 @@
public interface AslMarshallableFactory<T extends AslMarshallable> {
- /** Creates an {@link AslMarshallableFactory} from human-readable DOM element */
+ /** Creates an {@link AslMarshallableFactory} from human-readable DOM elements */
T createFromHrElements(List<Element> elements) throws MalformedXmlException;
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ T createFromOdElements(List<Element> elements) throws MalformedXmlException;
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java
index 4d67162..d551953 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java
@@ -59,4 +59,12 @@
}
return XmlUtils.listOf(dataCategoryEle);
}
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ throw new IllegalStateException(
+ "Turning DataCategory or DataType into human-readable DOM elements requires"
+ + " visibility into parent elements. The logic resides in DataLabels.");
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java
index 37d99e7..90424fe 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java
@@ -50,4 +50,31 @@
return new DataCategory(categoryName, dataTypeMap);
}
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public DataCategory createFromOdElements(List<Element> elements) throws MalformedXmlException {
+ Element dataCategoryEle = XmlUtils.getSingleElement(elements);
+ Map<String, DataType> dataTypeMap = new LinkedHashMap<String, DataType>();
+ String categoryName = dataCategoryEle.getAttribute(XmlUtils.OD_ATTR_NAME);
+ var odDataTypes = XmlUtils.asElementList(dataCategoryEle.getChildNodes());
+ for (Element odDataTypeEle : odDataTypes) {
+ String dataTypeName = odDataTypeEle.getAttribute(XmlUtils.OD_ATTR_NAME);
+ if (!DataTypeConstants.getValidDataTypes().containsKey(categoryName)) {
+ throw new MalformedXmlException(
+ String.format("Unrecognized data category %s", categoryName));
+ }
+ if (!DataTypeConstants.getValidDataTypes().get(categoryName).contains(dataTypeName)) {
+ throw new MalformedXmlException(
+ String.format(
+ "Unrecognized data type name %s for category %s",
+ dataTypeName, categoryName));
+ }
+ dataTypeMap.put(
+ dataTypeName,
+ new DataTypeFactory().createFromOdElements(XmlUtils.listOf(odDataTypeEle)));
+ }
+
+ return new DataCategory(categoryName, dataTypeMap);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
index 7516faf..4a0d759 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
@@ -79,6 +79,16 @@
return XmlUtils.listOf(dataLabelsEle);
}
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element dataLabelsEle = doc.createElement(XmlUtils.HR_TAG_DATA_LABELS);
+ maybeAppendHrDataUsages(doc, dataLabelsEle, mDataAccessed, XmlUtils.HR_TAG_DATA_ACCESSED);
+ maybeAppendHrDataUsages(doc, dataLabelsEle, mDataCollected, XmlUtils.HR_TAG_DATA_COLLECTED);
+ maybeAppendHrDataUsages(doc, dataLabelsEle, mDataShared, XmlUtils.HR_TAG_DATA_SHARED);
+ return XmlUtils.listOf(dataLabelsEle);
+ }
+
private void maybeAppendDataUsages(
Document doc,
Element dataLabelsEle,
@@ -100,4 +110,42 @@
}
dataLabelsEle.appendChild(dataUsageEle);
}
+
+ private void maybeAppendHrDataUsages(
+ Document doc,
+ Element dataLabelsEle,
+ Map<String, DataCategory> dataCategoriesMap,
+ String dataUsageTypeName) {
+ if (dataCategoriesMap.isEmpty()) {
+ return;
+ }
+ for (String dataCategoryName : dataCategoriesMap.keySet()) {
+ DataCategory dataCategory = dataCategoriesMap.get(dataCategoryName);
+ for (String dataTypeName : dataCategory.getDataTypes().keySet()) {
+ DataType dataType = dataCategory.getDataTypes().get(dataTypeName);
+ // XmlUtils.appendChildren(dataLabelsEle, dataType.toHrDomElements(doc));
+ Element hrDataTypeEle = doc.createElement(dataUsageTypeName);
+ hrDataTypeEle.setAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY, dataCategoryName);
+ hrDataTypeEle.setAttribute(XmlUtils.HR_ATTR_DATA_TYPE, dataTypeName);
+ XmlUtils.maybeSetHrBoolAttr(
+ hrDataTypeEle,
+ XmlUtils.HR_ATTR_IS_COLLECTION_OPTIONAL,
+ dataType.getIsCollectionOptional());
+ XmlUtils.maybeSetHrBoolAttr(
+ hrDataTypeEle,
+ XmlUtils.HR_ATTR_IS_SHARING_OPTIONAL,
+ dataType.getIsSharingOptional());
+ XmlUtils.maybeSetHrBoolAttr(
+ hrDataTypeEle, XmlUtils.HR_ATTR_EPHEMERAL, dataType.getEphemeral());
+ hrDataTypeEle.setAttribute(
+ XmlUtils.HR_ATTR_PURPOSES,
+ String.join(
+ "|",
+ dataType.getPurposes().stream()
+ .map(DataType.Purpose::toString)
+ .toList()));
+ dataLabelsEle.appendChild(hrDataTypeEle);
+ }
+ }
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java
index dc77fd0..5473e01 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java
@@ -22,7 +22,6 @@
import com.android.asllib.util.XmlUtils;
import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -46,60 +45,57 @@
getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_COLLECTED);
Map<String, DataCategory> dataShared =
getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_SHARED);
+ DataLabels dataLabels = new DataLabels(dataAccessed, dataCollected, dataShared);
+ validateIsXOptional(dataLabels);
+ return dataLabels;
+ }
- // Validate booleans such as isCollectionOptional, isSharingOptional.
- for (DataCategory dataCategory : dataAccessed.values()) {
- for (DataType dataType : dataCategory.getDataTypes().values()) {
- if (dataType.getIsSharingOptional() != null) {
- throw new MalformedXmlException(
- String.format(
- "isSharingOptional was unexpectedly defined on a DataType"
- + " belonging to data accessed: %s",
- dataType.getDataTypeName()));
- }
- if (dataType.getIsCollectionOptional() != null) {
- throw new MalformedXmlException(
- String.format(
- "isCollectionOptional was unexpectedly defined on a DataType"
- + " belonging to data accessed: %s",
- dataType.getDataTypeName()));
- }
- }
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public DataLabels createFromOdElements(List<Element> elements) throws MalformedXmlException {
+ Element dataLabelsEle = XmlUtils.getSingleElement(elements);
+ if (dataLabelsEle == null) {
+ AslgenUtil.logI("Found no DataLabels in od format.");
+ return null;
}
- for (DataCategory dataCategory : dataCollected.values()) {
- for (DataType dataType : dataCategory.getDataTypes().values()) {
- if (dataType.getIsSharingOptional() != null) {
- throw new MalformedXmlException(
- String.format(
- "isSharingOptional was unexpectedly defined on a DataType"
- + " belonging to data collected: %s",
- dataType.getDataTypeName()));
- }
- }
- }
- for (DataCategory dataCategory : dataShared.values()) {
- for (DataType dataType : dataCategory.getDataTypes().values()) {
- if (dataType.getIsCollectionOptional() != null) {
- throw new MalformedXmlException(
- String.format(
- "isCollectionOptional was unexpectedly defined on a DataType"
- + " belonging to data shared: %s",
- dataType.getDataTypeName()));
- }
- }
- }
+ Map<String, DataCategory> dataAccessed =
+ getOdDataCategoriesWithTag(dataLabelsEle, XmlUtils.OD_NAME_DATA_ACCESSED);
+ Map<String, DataCategory> dataCollected =
+ getOdDataCategoriesWithTag(dataLabelsEle, XmlUtils.OD_NAME_DATA_COLLECTED);
+ Map<String, DataCategory> dataShared =
+ getOdDataCategoriesWithTag(dataLabelsEle, XmlUtils.OD_NAME_DATA_SHARED);
+ DataLabels dataLabels = new DataLabels(dataAccessed, dataCollected, dataShared);
+ validateIsXOptional(dataLabels);
+ return dataLabels;
+ }
- return new DataLabels(dataAccessed, dataCollected, dataShared);
+ private static Map<String, DataCategory> getOdDataCategoriesWithTag(
+ Element dataLabelsEle, String dataCategoryUsageTypeTag) throws MalformedXmlException {
+ Map<String, DataCategory> dataCategoryMap = new LinkedHashMap<String, DataCategory>();
+ Element dataUsageEle =
+ XmlUtils.getOdPbundleWithName(dataLabelsEle, dataCategoryUsageTypeTag, false);
+ if (dataUsageEle == null) {
+ return dataCategoryMap;
+ }
+ List<Element> dataCategoryEles = XmlUtils.asElementList(dataUsageEle.getChildNodes());
+ for (Element dataCategoryEle : dataCategoryEles) {
+ String dataCategoryName = dataCategoryEle.getAttribute(XmlUtils.OD_ATTR_NAME);
+ DataCategory dataCategory =
+ new DataCategoryFactory().createFromOdElements(List.of(dataCategoryEle));
+ dataCategoryMap.put(dataCategoryName, dataCategory);
+ }
+ return dataCategoryMap;
}
private static Map<String, DataCategory> getDataCategoriesWithTag(
Element dataLabelsEle, String dataCategoryUsageTypeTag) throws MalformedXmlException {
- NodeList dataUsedNodeList = dataLabelsEle.getElementsByTagName(dataCategoryUsageTypeTag);
+ List<Element> dataUsedElements =
+ XmlUtils.getChildrenByTagName(dataLabelsEle, dataCategoryUsageTypeTag);
Map<String, DataCategory> dataCategoryMap = new LinkedHashMap<String, DataCategory>();
Set<String> dataCategoryNames = new HashSet<String>();
- for (int i = 0; i < dataUsedNodeList.getLength(); i++) {
- Element dataUsedEle = (Element) dataUsedNodeList.item(i);
+ for (int i = 0; i < dataUsedElements.size(); i++) {
+ Element dataUsedEle = dataUsedElements.get(i);
String dataCategoryName = dataUsedEle.getAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY);
if (!DataCategoryConstants.getValidDataCategories().contains(dataCategoryName)) {
throw new MalformedXmlException(
@@ -109,7 +105,7 @@
}
for (String dataCategoryName : dataCategoryNames) {
var dataCategoryElements =
- XmlUtils.asElementList(dataUsedNodeList).stream()
+ dataUsedElements.stream()
.filter(
ele ->
ele.getAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY)
@@ -121,4 +117,48 @@
}
return dataCategoryMap;
}
+
+ private void validateIsXOptional(DataLabels dataLabels) throws MalformedXmlException {
+ // Validate booleans such as isCollectionOptional, isSharingOptional.
+ for (DataCategory dataCategory : dataLabels.getDataAccessed().values()) {
+ for (DataType dataType : dataCategory.getDataTypes().values()) {
+ if (dataType.getIsSharingOptional() != null) {
+ throw new MalformedXmlException(
+ String.format(
+ "isSharingOptional was unexpectedly defined on a DataType"
+ + " belonging to data accessed: %s",
+ dataType.getDataTypeName()));
+ }
+ if (dataType.getIsCollectionOptional() != null) {
+ throw new MalformedXmlException(
+ String.format(
+ "isCollectionOptional was unexpectedly defined on a DataType"
+ + " belonging to data accessed: %s",
+ dataType.getDataTypeName()));
+ }
+ }
+ }
+ for (DataCategory dataCategory : dataLabels.getDataCollected().values()) {
+ for (DataType dataType : dataCategory.getDataTypes().values()) {
+ if (dataType.getIsSharingOptional() != null) {
+ throw new MalformedXmlException(
+ String.format(
+ "isSharingOptional was unexpectedly defined on a DataType"
+ + " belonging to data collected: %s",
+ dataType.getDataTypeName()));
+ }
+ }
+ }
+ for (DataCategory dataCategory : dataLabels.getDataShared().values()) {
+ for (DataType dataType : dataCategory.getDataTypes().values()) {
+ if (dataType.getIsCollectionOptional() != null) {
+ throw new MalformedXmlException(
+ String.format(
+ "isCollectionOptional was unexpectedly defined on a DataType"
+ + " belonging to data shared: %s",
+ dataType.getDataTypeName()));
+ }
+ }
+ }
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java
index 3471362..97304cb 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java
@@ -160,6 +160,14 @@
return XmlUtils.listOf(dataTypeEle);
}
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ throw new IllegalStateException(
+ "Turning DataCategory or DataType into human-readable DOM elements requires"
+ + " visibility into parent elements. The logic resides in DataLabels.");
+ }
+
private static void maybeAddBoolToOdElement(
Document doc, Element parentEle, Boolean b, String odName) {
if (b == null) {
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java
index ed434cd..488c259 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java
@@ -51,4 +51,31 @@
return new DataType(
dataTypeName, purposes, isCollectionOptional, isSharingOptional, ephemeral);
}
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public DataType createFromOdElements(List<Element> elements) throws MalformedXmlException {
+ Element odDataTypeEle = XmlUtils.getSingleElement(elements);
+ String dataTypeName = odDataTypeEle.getAttribute(XmlUtils.OD_ATTR_NAME);
+ List<Integer> purposeInts =
+ XmlUtils.getOdIntArray(odDataTypeEle, XmlUtils.OD_NAME_PURPOSES, true);
+ List<DataType.Purpose> purposes =
+ purposeInts.stream().map(DataType.Purpose::forValue).collect(Collectors.toList());
+ if (purposes.isEmpty()) {
+ throw new MalformedXmlException(String.format("Found no purpose in: %s", dataTypeName));
+ }
+ if (new HashSet<>(purposes).size() != purposes.size()) {
+ throw new MalformedXmlException(
+ String.format("Found non-unique purposes in: %s", dataTypeName));
+ }
+ Boolean isCollectionOptional =
+ XmlUtils.getOdBoolEle(
+ odDataTypeEle, XmlUtils.OD_NAME_IS_COLLECTION_OPTIONAL, false);
+ Boolean isSharingOptional =
+ XmlUtils.getOdBoolEle(odDataTypeEle, XmlUtils.OD_NAME_IS_SHARING_OPTIONAL, false);
+ Boolean ephemeral = XmlUtils.getOdBoolEle(odDataTypeEle, XmlUtils.OD_NAME_EPHEMERAL, false);
+
+ return new DataType(
+ dataTypeName, purposes, isCollectionOptional, isSharingOptional, ephemeral);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfo.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfo.java
index 382a1f0..94fad96 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfo.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfo.java
@@ -139,4 +139,35 @@
return XmlUtils.listOf(developerInfoEle);
}
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element developerInfoEle = doc.createElement(XmlUtils.HR_TAG_DEVELOPER_INFO);
+ if (mName != null) {
+ developerInfoEle.setAttribute(XmlUtils.HR_ATTR_NAME, mName);
+ }
+ if (mEmail != null) {
+ developerInfoEle.setAttribute(XmlUtils.HR_ATTR_EMAIL, mEmail);
+ }
+ if (mAddress != null) {
+ developerInfoEle.setAttribute(XmlUtils.HR_ATTR_ADDRESS, mAddress);
+ }
+ if (mCountryRegion != null) {
+ developerInfoEle.setAttribute(XmlUtils.HR_ATTR_COUNTRY_REGION, mCountryRegion);
+ }
+ if (mDeveloperRelationship != null) {
+ developerInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_DEVELOPER_RELATIONSHIP, mDeveloperRelationship.toString());
+ }
+ if (mWebsite != null) {
+ developerInfoEle.setAttribute(XmlUtils.HR_ATTR_WEBSITE, mWebsite);
+ }
+ if (mAppDeveloperRegistryId != null) {
+ developerInfoEle.setAttribute(
+ XmlUtils.HR_ATTR_APP_DEVELOPER_REGISTRY_ID, mAppDeveloperRegistryId);
+ }
+
+ return XmlUtils.listOf(developerInfoEle);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfoFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfoFactory.java
index b5310ba..0f3b41c 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfoFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfoFactory.java
@@ -34,15 +34,15 @@
AslgenUtil.logI("No DeveloperInfo found in hr format.");
return null;
}
- String name = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_NAME);
- String email = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_EMAIL);
- String address = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_ADDRESS);
+ String name = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_NAME, true);
+ String email = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_EMAIL, true);
+ String address = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_ADDRESS, true);
String countryRegion =
- XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_COUNTRY_REGION);
+ XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_COUNTRY_REGION, true);
DeveloperInfo.DeveloperRelationship developerRelationship =
DeveloperInfo.DeveloperRelationship.forString(
XmlUtils.getStringAttr(
- developerInfoEle, XmlUtils.HR_ATTR_DEVELOPER_RELATIONSHIP));
+ developerInfoEle, XmlUtils.HR_ATTR_DEVELOPER_RELATIONSHIP, true));
String website = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_WEBSITE, false);
String appDeveloperRegistryId =
XmlUtils.getStringAttr(
@@ -57,4 +57,40 @@
website,
appDeveloperRegistryId);
}
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public DeveloperInfo createFromOdElements(List<Element> elements) throws MalformedXmlException {
+ Element developerInfoEle = XmlUtils.getSingleElement(elements);
+ if (developerInfoEle == null) {
+ AslgenUtil.logI("No DeveloperInfo found in od format.");
+ return null;
+ }
+ String name = XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_NAME, true);
+ String email = XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_EMAIL, true);
+ String address = XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_ADDRESS, true);
+ String countryRegion =
+ XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_COUNTRY_REGION, true);
+ DeveloperInfo.DeveloperRelationship developerRelationship =
+ DeveloperInfo.DeveloperRelationship.forValue(
+ (int)
+ (long)
+ XmlUtils.getOdLongEle(
+ developerInfoEle,
+ XmlUtils.OD_NAME_DEVELOPER_RELATIONSHIP,
+ true));
+ String website = XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_WEBSITE, false);
+ String appDeveloperRegistryId =
+ XmlUtils.getOdStringEle(
+ developerInfoEle, XmlUtils.OD_NAME_APP_DEVELOPER_REGISTRY_ID, false);
+
+ return new DeveloperInfo(
+ name,
+ email,
+ address,
+ countryRegion,
+ developerRelationship,
+ website,
+ appDeveloperRegistryId);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabels.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabels.java
index 8c2186a..6af8071 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabels.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabels.java
@@ -70,4 +70,22 @@
}
return XmlUtils.listOf(safetyLabelsEle);
}
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element safetyLabelsEle = doc.createElement(XmlUtils.HR_TAG_SAFETY_LABELS);
+ safetyLabelsEle.setAttribute(XmlUtils.HR_ATTR_VERSION, String.valueOf(mVersion));
+
+ if (mDataLabels != null) {
+ XmlUtils.appendChildren(safetyLabelsEle, mDataLabels.toHrDomElements(doc));
+ }
+ if (mSecurityLabels != null) {
+ XmlUtils.appendChildren(safetyLabelsEle, mSecurityLabels.toHrDomElements(doc));
+ }
+ if (mThirdPartyVerification != null) {
+ XmlUtils.appendChildren(safetyLabelsEle, mThirdPartyVerification.toHrDomElements(doc));
+ }
+ return XmlUtils.listOf(safetyLabelsEle);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabelsFactory.java
index 0f7aa81..2644b43 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabelsFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabelsFactory.java
@@ -62,4 +62,41 @@
false)));
return new SafetyLabels(version, dataLabels, securityLabels, thirdPartyVerification);
}
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public SafetyLabels createFromOdElements(List<Element> elements) throws MalformedXmlException {
+ Element safetyLabelsEle = XmlUtils.getSingleElement(elements);
+ if (safetyLabelsEle == null) {
+ AslgenUtil.logI("No SafetyLabels found in od format.");
+ return null;
+ }
+ Long version = XmlUtils.getOdLongEle(safetyLabelsEle, XmlUtils.OD_NAME_VERSION, true);
+
+ DataLabels dataLabels =
+ new DataLabelsFactory()
+ .createFromOdElements(
+ XmlUtils.listOf(
+ XmlUtils.getOdPbundleWithName(
+ safetyLabelsEle,
+ XmlUtils.OD_NAME_DATA_LABELS,
+ false)));
+ SecurityLabels securityLabels =
+ new SecurityLabelsFactory()
+ .createFromOdElements(
+ XmlUtils.listOf(
+ XmlUtils.getOdPbundleWithName(
+ safetyLabelsEle,
+ XmlUtils.OD_NAME_SECURITY_LABELS,
+ false)));
+ ThirdPartyVerification thirdPartyVerification =
+ new ThirdPartyVerificationFactory()
+ .createFromOdElements(
+ XmlUtils.listOf(
+ XmlUtils.getOdPbundleWithName(
+ safetyLabelsEle,
+ XmlUtils.OD_NAME_THIRD_PARTY_VERIFICATION,
+ false)));
+ return new SafetyLabels(version, dataLabels, securityLabels, thirdPartyVerification);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabels.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabels.java
index 529b5036..48643ba 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabels.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabels.java
@@ -50,4 +50,17 @@
}
return XmlUtils.listOf(ele);
}
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element ele = doc.createElement(XmlUtils.HR_TAG_SECURITY_LABELS);
+ if (mIsDataDeletable != null) {
+ ele.setAttribute(XmlUtils.HR_ATTR_IS_DATA_DELETABLE, String.valueOf(mIsDataDeletable));
+ }
+ if (mIsDataEncrypted != null) {
+ ele.setAttribute(XmlUtils.HR_ATTR_IS_DATA_ENCRYPTED, String.valueOf(mIsDataEncrypted));
+ }
+ return XmlUtils.listOf(ele);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabelsFactory.java
index 8402452..525a803 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabelsFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SecurityLabelsFactory.java
@@ -41,4 +41,20 @@
XmlUtils.getBoolAttr(ele, XmlUtils.HR_ATTR_IS_DATA_ENCRYPTED, false);
return new SecurityLabels(isDataDeletable, isDataEncrypted);
}
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public SecurityLabels createFromOdElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element ele = XmlUtils.getSingleElement(elements);
+ if (ele == null) {
+ AslgenUtil.logI("No SecurityLabels found in od format.");
+ return null;
+ }
+ Boolean isDataDeletable =
+ XmlUtils.getOdBoolEle(ele, XmlUtils.OD_NAME_IS_DATA_DELETABLE, false);
+ Boolean isDataEncrypted =
+ XmlUtils.getOdBoolEle(ele, XmlUtils.OD_NAME_IS_DATA_ENCRYPTED, false);
+ return new SecurityLabels(isDataDeletable, isDataEncrypted);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java
index 595d748..854c0d0 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java
@@ -46,4 +46,13 @@
XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_URL, mUrl));
return XmlUtils.listOf(systemAppSafetyLabelEle);
}
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element systemAppSafetyLabelEle =
+ doc.createElement(XmlUtils.HR_TAG_SYSTEM_APP_SAFETY_LABEL);
+ systemAppSafetyLabelEle.setAttribute(XmlUtils.HR_ATTR_URL, mUrl);
+ return XmlUtils.listOf(systemAppSafetyLabelEle);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java
index f999559..c8e22b6 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java
@@ -36,7 +36,20 @@
return null;
}
- String url = XmlUtils.getStringAttr(systemAppSafetyLabelEle, XmlUtils.HR_ATTR_URL);
+ String url = XmlUtils.getStringAttr(systemAppSafetyLabelEle, XmlUtils.HR_ATTR_URL, true);
+ return new SystemAppSafetyLabel(url);
+ }
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public SystemAppSafetyLabel createFromOdElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element systemAppSafetyLabelEle = XmlUtils.getSingleElement(elements);
+ if (systemAppSafetyLabelEle == null) {
+ AslgenUtil.logI("No SystemAppSafetyLabel found in od format.");
+ return null;
+ }
+ String url = XmlUtils.getOdStringEle(systemAppSafetyLabelEle, XmlUtils.OD_NAME_URL, true);
return new SystemAppSafetyLabel(url);
}
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerification.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerification.java
index a1b22f8..d74f3f0 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerification.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerification.java
@@ -40,4 +40,12 @@
ele.appendChild(XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_URL, mUrl));
return XmlUtils.listOf(ele);
}
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element ele = doc.createElement(XmlUtils.HR_TAG_THIRD_PARTY_VERIFICATION);
+ ele.setAttribute(XmlUtils.HR_ATTR_URL, mUrl);
+ return XmlUtils.listOf(ele);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerificationFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerificationFactory.java
index c3e4964..197e7aa 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerificationFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/ThirdPartyVerificationFactory.java
@@ -37,7 +37,21 @@
return null;
}
- String url = XmlUtils.getStringAttr(ele, XmlUtils.HR_ATTR_URL);
+ String url = XmlUtils.getStringAttr(ele, XmlUtils.HR_ATTR_URL, true);
+ return new ThirdPartyVerification(url);
+ }
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public ThirdPartyVerification createFromOdElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element ele = XmlUtils.getSingleElement(elements);
+ if (ele == null) {
+ AslgenUtil.logI("No ThirdPartyVerification found in od format.");
+ return null;
+ }
+
+ String url = XmlUtils.getOdStringEle(ele, XmlUtils.OD_NAME_URL, true);
return new ThirdPartyVerification(url);
}
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfo.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfo.java
index ddd3557..6a8700a 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfo.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfo.java
@@ -57,4 +57,17 @@
}
return XmlUtils.listOf(transparencyInfoEle);
}
+
+ /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
+ @Override
+ public List<Element> toHrDomElements(Document doc) {
+ Element transparencyInfoEle = doc.createElement(XmlUtils.HR_TAG_TRANSPARENCY_INFO);
+ if (mDeveloperInfo != null) {
+ XmlUtils.appendChildren(transparencyInfoEle, mDeveloperInfo.toHrDomElements(doc));
+ }
+ if (mAppInfo != null) {
+ XmlUtils.appendChildren(transparencyInfoEle, mAppInfo.toHrDomElements(doc));
+ }
+ return XmlUtils.listOf(transparencyInfoEle);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfoFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfoFactory.java
index d9c2af4..94c5640 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfoFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfoFactory.java
@@ -49,4 +49,28 @@
return new TransparencyInfo(developerInfo, appInfo);
}
+
+ /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
+ @Override
+ public TransparencyInfo createFromOdElements(List<Element> elements)
+ throws MalformedXmlException {
+ Element transparencyInfoEle = XmlUtils.getSingleElement(elements);
+ if (transparencyInfoEle == null) {
+ AslgenUtil.logI("No TransparencyInfo found in od format.");
+ return null;
+ }
+
+ Element developerInfoEle =
+ XmlUtils.getOdPbundleWithName(
+ transparencyInfoEle, XmlUtils.OD_NAME_DEVELOPER_INFO, false);
+ DeveloperInfo developerInfo =
+ new DeveloperInfoFactory().createFromOdElements(XmlUtils.listOf(developerInfoEle));
+
+ Element appInfoEle =
+ XmlUtils.getOdPbundleWithName(
+ transparencyInfoEle, XmlUtils.OD_NAME_APP_INFO, false);
+ AppInfo appInfo = new AppInfoFactory().createFromOdElements(XmlUtils.listOf(appInfoEle));
+
+ return new TransparencyInfo(developerInfo, appInfo);
+ }
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
index ed3d7f8..1d54ead 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
@@ -16,8 +16,10 @@
package com.android.asllib.util;
+
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.ArrayList;
@@ -118,59 +120,37 @@
public static final String TRUE_STR = "true";
public static final String FALSE_STR = "false";
- /** Gets the single top-level {@link Element} having the {@param tagName}. */
- public static Element getSingleElement(Document doc, String tagName)
- throws MalformedXmlException {
- var elements = doc.getElementsByTagName(tagName);
- return getSingleElement(elements, tagName);
+ /** Gets the top-level children with the tag name.. */
+ public static List<Element> getChildrenByTagName(Node parentEle, String tagName) {
+ var elements = XmlUtils.asElementList(parentEle.getChildNodes());
+ return elements.stream().filter(e -> e.getTagName().equals(tagName)).toList();
}
/**
* Gets the single {@link Element} within {@param parentEle} and having the {@param tagName}.
*/
- public static Element getSingleChildElement(Element parentEle, String tagName)
+ public static Element getSingleChildElement(Node parentEle, String tagName, boolean required)
throws MalformedXmlException {
- var elements = parentEle.getElementsByTagName(tagName);
- return getSingleElement(elements, tagName, true);
- }
+ String parentTagNameForErrorMsg =
+ (parentEle instanceof Element) ? ((Element) parentEle).getTagName() : "Node";
+ var elements = getChildrenByTagName(parentEle, tagName);
- /**
- * Gets the single {@link Element} within {@param parentEle} and having the {@param tagName}.
- */
- public static Element getSingleChildElement(Element parentEle, String tagName, boolean required)
- throws MalformedXmlException {
- var elements = parentEle.getElementsByTagName(tagName);
- return getSingleElement(elements, tagName, required);
- }
-
- /** Gets the single {@link Element} from {@param elements} */
- public static Element getSingleElement(NodeList elements, String tagName)
- throws MalformedXmlException {
- return getSingleElement(elements, tagName, true);
- }
-
- /** Gets the single {@link Element} from {@param elements} */
- public static Element getSingleElement(NodeList elements, String tagName, boolean required)
- throws MalformedXmlException {
- if (elements.getLength() > 1) {
+ if (elements.size() > 1) {
throw new MalformedXmlException(
String.format(
- "Expected 1 element \"%s\" in NodeList but got %s.",
- tagName, elements.getLength()));
- } else if (elements.getLength() == 0) {
+ "Expected 1 %s in %s but got %s.",
+ tagName, parentTagNameForErrorMsg, elements.size()));
+ } else if (elements.isEmpty()) {
if (required) {
throw new MalformedXmlException(
- String.format("Found no element \"%s\" in NodeList.", tagName));
+ String.format(
+ "Expected 1 %s in %s but got 0.",
+ tagName, parentTagNameForErrorMsg));
} else {
return null;
}
}
- var elementAsNode = elements.item(0);
- if (!(elementAsNode instanceof Element)) {
- throw new MalformedXmlException(
- String.format("%s was not a valid XML element.", tagName));
- }
- return ((Element) elementAsNode);
+ return elements.get(0);
}
/** Gets the single {@link Element} within {@param elements}. */
@@ -229,6 +209,13 @@
return ele;
}
+ /** Sets human-readable bool attribute if non-null. */
+ public static void maybeSetHrBoolAttr(Element ele, String attrName, Boolean b) {
+ if (b != null) {
+ ele.setAttribute(attrName, String.valueOf(b));
+ }
+ }
+
/** Create an on-device Long DOM Element with the given attribute name. */
public static Element createOdLongEle(Document doc, String name, long l) {
var ele = doc.createElement(XmlUtils.OD_TAG_LONG);
@@ -303,6 +290,114 @@
return b;
}
+ /** Gets a Boolean attribute. */
+ public static Boolean getOdBoolEle(Element ele, String nameName, boolean required)
+ throws MalformedXmlException {
+ List<Element> boolEles =
+ XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_BOOLEAN).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
+ .toList();
+ if (boolEles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+ }
+ if (boolEles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format("Found no %s in %s.", nameName, ele.getTagName()));
+ }
+ return null;
+ }
+ Element boolEle = boolEles.get(0);
+
+ Boolean b = XmlUtils.fromString(boolEle.getAttribute(XmlUtils.OD_ATTR_VALUE));
+ if (b == null && required) {
+ throw new MalformedXmlException(
+ String.format(
+ "Boolean %s was required but missing, in %s.",
+ nameName, ele.getTagName()));
+ }
+ return b;
+ }
+
+ /** Gets an on-device Long attribute. */
+ public static Long getOdLongEle(Element ele, String nameName, boolean required)
+ throws MalformedXmlException {
+ List<Element> longEles =
+ XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_LONG).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
+ .toList();
+ if (longEles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+ }
+ if (longEles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format("Found no %s in %s.", nameName, ele.getTagName()));
+ }
+ return null;
+ }
+ Element longEle = longEles.get(0);
+ Long l = null;
+ try {
+ l = Long.parseLong(longEle.getAttribute(XmlUtils.OD_ATTR_VALUE));
+ } catch (NumberFormatException e) {
+ throw new MalformedXmlException(
+ String.format(
+ "%s in %s was not formatted as long", nameName, ele.getTagName()));
+ }
+ return l;
+ }
+
+ /** Gets an on-device String attribute. */
+ public static String getOdStringEle(Element ele, String nameName, boolean required)
+ throws MalformedXmlException {
+ List<Element> eles =
+ XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_STRING).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
+ .toList();
+ if (eles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+ }
+ if (eles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format("Found no %s in %s.", nameName, ele.getTagName()));
+ }
+ return null;
+ }
+ String str = eles.get(0).getAttribute(XmlUtils.OD_ATTR_VALUE);
+ if (XmlUtils.isNullOrEmpty(str) && required) {
+ throw new MalformedXmlException(
+ String.format(
+ "%s in %s was empty or missing value", nameName, ele.getTagName()));
+ }
+ return str;
+ }
+
+ /** Gets a OD Pbundle Element attribute with the specified name. */
+ public static Element getOdPbundleWithName(Element ele, String nameName, boolean required)
+ throws MalformedXmlException {
+ List<Element> eles =
+ XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_PBUNDLE_AS_MAP).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
+ .toList();
+ if (eles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+ }
+ if (eles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format("Found no %s in %s.", nameName, ele.getTagName()));
+ }
+ return null;
+ }
+ return eles.get(0);
+ }
+
/** Gets a required String attribute. */
public static String getStringAttr(Element ele, String attrName) throws MalformedXmlException {
return getStringAttr(ele, attrName, true);
@@ -325,6 +420,60 @@
return s;
}
+ /** Gets on-device style int array. */
+ public static List<Integer> getOdIntArray(Element ele, String nameName, boolean required)
+ throws MalformedXmlException {
+ List<Element> intArrayEles =
+ XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_INT_ARRAY).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
+ .toList();
+ if (intArrayEles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+ }
+ if (intArrayEles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format("Found no %s in %s.", nameName, ele.getTagName()));
+ }
+ return null;
+ }
+ Element intArrayEle = intArrayEles.get(0);
+ List<Element> itemEles = XmlUtils.getChildrenByTagName(intArrayEle, XmlUtils.OD_TAG_ITEM);
+ List<Integer> ints = new ArrayList<Integer>();
+ for (Element itemEle : itemEles) {
+ ints.add(Integer.parseInt(XmlUtils.getStringAttr(itemEle, XmlUtils.OD_ATTR_VALUE)));
+ }
+ return ints;
+ }
+
+ /** Gets on-device style String array. */
+ public static List<String> getOdStringArray(Element ele, String nameName, boolean required)
+ throws MalformedXmlException {
+ List<Element> arrayEles =
+ XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_STRING_ARRAY).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
+ .toList();
+ if (arrayEles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+ }
+ if (arrayEles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format("Found no %s in %s.", nameName, ele.getTagName()));
+ }
+ return null;
+ }
+ Element arrayEle = arrayEles.get(0);
+ List<Element> itemEles = XmlUtils.getChildrenByTagName(arrayEle, XmlUtils.OD_TAG_ITEM);
+ List<String> strs = new ArrayList<String>();
+ for (Element itemEle : itemEles) {
+ strs.add(XmlUtils.getStringAttr(itemEle, XmlUtils.OD_ATTR_VALUE, true));
+ }
+ return strs;
+ }
+
/**
* Utility method for making a List from one element, to support easier refactoring if needed.
* For example, List.of() doesn't support null elements.
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java
index e2588d7..d2e0fc3 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java
@@ -56,18 +56,35 @@
InputStream hrStream =
getClass().getClassLoader().getResourceAsStream(hrPath.toString());
- String hrContents = new String(hrStream.readAllBytes(), StandardCharsets.UTF_8);
+ String hrContents =
+ TestUtils.getFormattedXml(
+ new String(hrStream.readAllBytes(), StandardCharsets.UTF_8), false);
InputStream odStream =
getClass().getClassLoader().getResourceAsStream(odPath.toString());
- String odContents = new String(odStream.readAllBytes(), StandardCharsets.UTF_8);
- AndroidSafetyLabel asl =
+ String odContents =
+ TestUtils.getFormattedXml(
+ new String(odStream.readAllBytes(), StandardCharsets.UTF_8), false);
+ AndroidSafetyLabel aslFromHr =
AslConverter.readFromString(hrContents, AslConverter.Format.HUMAN_READABLE);
- String out = AslConverter.getXmlAsString(asl, AslConverter.Format.ON_DEVICE);
- System.out.println("out: " + out);
+ String aslToOdStr =
+ TestUtils.getFormattedXml(
+ AslConverter.getXmlAsString(aslFromHr, AslConverter.Format.ON_DEVICE),
+ false);
+ AndroidSafetyLabel aslFromOd =
+ AslConverter.readFromString(odContents, AslConverter.Format.ON_DEVICE);
+ String aslToHrStr =
+ TestUtils.getFormattedXml(
+ AslConverter.getXmlAsString(
+ aslFromOd, AslConverter.Format.HUMAN_READABLE),
+ false);
- assertEquals(
- TestUtils.getFormattedXml(out, false),
- TestUtils.getFormattedXml(odContents, false));
+ System.out.println("od expected: " + odContents);
+ System.out.println("asl to od: " + aslToOdStr);
+ assertEquals(odContents, aslToOdStr);
+
+ System.out.println("hr expected: " + hrContents);
+ System.out.println("asl to hr: " + aslToHrStr);
+ assertEquals(hrContents, aslToHrStr);
}
}
}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AndroidSafetyLabelTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AndroidSafetyLabelTest.java
index 0137007..61a7823 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AndroidSafetyLabelTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AndroidSafetyLabelTest.java
@@ -22,7 +22,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.w3c.dom.Document;
@RunWith(JUnit4.class)
public class AndroidSafetyLabelTest {
@@ -38,12 +37,9 @@
"with-system-app-safety-label.xml";
private static final String WITH_TRANSPARENCY_INFO_FILE_NAME = "with-transparency-info.xml";
- private Document mDoc = null;
-
@Before
public void setUp() throws Exception {
System.out.println("set up.");
- mDoc = TestUtils.document();
}
/** Test for android safety label missing version. */
@@ -51,6 +47,7 @@
public void testAndroidSafetyLabelMissingVersion() throws Exception {
System.out.println("starting testAndroidSafetyLabelMissingVersion.");
hrToOdExpectException(MISSING_VERSION_FILE_NAME);
+ odToHrExpectException(MISSING_VERSION_FILE_NAME);
}
/** Test for android safety label valid empty. */
@@ -58,6 +55,7 @@
public void testAndroidSafetyLabelValidEmptyFile() throws Exception {
System.out.println("starting testAndroidSafetyLabelValidEmptyFile.");
testHrToOdAndroidSafetyLabel(VALID_EMPTY_FILE_NAME);
+ testOdToHrAndroidSafetyLabel(VALID_EMPTY_FILE_NAME);
}
/** Test for android safety label with safety labels. */
@@ -65,6 +63,7 @@
public void testAndroidSafetyLabelWithSafetyLabels() throws Exception {
System.out.println("starting testAndroidSafetyLabelWithSafetyLabels.");
testHrToOdAndroidSafetyLabel(WITH_SAFETY_LABELS_FILE_NAME);
+ testOdToHrAndroidSafetyLabel(WITH_SAFETY_LABELS_FILE_NAME);
}
/** Test for android safety label with system app safety label. */
@@ -72,6 +71,7 @@
public void testAndroidSafetyLabelWithSystemAppSafetyLabel() throws Exception {
System.out.println("starting testAndroidSafetyLabelWithSystemAppSafetyLabel.");
testHrToOdAndroidSafetyLabel(WITH_SYSTEM_APP_SAFETY_LABEL_FILE_NAME);
+ testOdToHrAndroidSafetyLabel(WITH_SYSTEM_APP_SAFETY_LABEL_FILE_NAME);
}
/** Test for android safety label with transparency info. */
@@ -79,6 +79,7 @@
public void testAndroidSafetyLabelWithTransparencyInfo() throws Exception {
System.out.println("starting testAndroidSafetyLabelWithTransparencyInfo.");
testHrToOdAndroidSafetyLabel(WITH_TRANSPARENCY_INFO_FILE_NAME);
+ testOdToHrAndroidSafetyLabel(WITH_TRANSPARENCY_INFO_FILE_NAME);
}
private void hrToOdExpectException(String fileName) {
@@ -86,12 +87,26 @@
new AndroidSafetyLabelFactory(), ANDROID_SAFETY_LABEL_HR_PATH, fileName);
}
+ private void odToHrExpectException(String fileName) {
+ TestUtils.odToHrExpectException(
+ new AndroidSafetyLabelFactory(), ANDROID_SAFETY_LABEL_OD_PATH, fileName);
+ }
+
private void testHrToOdAndroidSafetyLabel(String fileName) throws Exception {
TestUtils.testHrToOd(
- mDoc,
+ TestUtils.document(),
new AndroidSafetyLabelFactory(),
ANDROID_SAFETY_LABEL_HR_PATH,
ANDROID_SAFETY_LABEL_OD_PATH,
fileName);
}
+
+ private void testOdToHrAndroidSafetyLabel(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new AndroidSafetyLabelFactory(),
+ ANDROID_SAFETY_LABEL_OD_PATH,
+ ANDROID_SAFETY_LABEL_HR_PATH,
+ fileName);
+ }
}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java
index a015e2e..9e91c6f 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java
@@ -25,7 +25,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.w3c.dom.Document;
import java.nio.file.Paths;
import java.util.List;
@@ -48,19 +47,31 @@
"serviceProviderEndpoints",
"category",
"email");
+ public static final List<String> REQUIRED_FIELD_NAMES_OD =
+ List.of(
+ "title",
+ "description",
+ "contains_ads",
+ "obey_aps",
+ "ads_fingerprinting",
+ "security_fingerprinting",
+ "privacy_policy",
+ "security_endpoint",
+ "first_party_endpoint",
+ "service_provider_endpoint",
+ "category",
+ "email");
public static final List<String> OPTIONAL_FIELD_NAMES = List.of("website");
+ public static final List<String> OPTIONAL_FIELD_NAMES_OD = List.of("website");
private static final String ALL_FIELDS_VALID_FILE_NAME = "all-fields-valid.xml";
- private Document mDoc = null;
-
/** Logic for setting up tests (empty if not yet needed). */
public static void main(String[] params) throws Exception {}
@Before
public void setUp() throws Exception {
System.out.println("set up.");
- mDoc = TestUtils.document();
}
/** Test for all fields valid. */
@@ -68,6 +79,7 @@
public void testAllFieldsValid() throws Exception {
System.out.println("starting testAllFieldsValid.");
testHrToOdAppInfo(ALL_FIELDS_VALID_FILE_NAME);
+ testOdToHrAppInfo(ALL_FIELDS_VALID_FILE_NAME);
}
/** Tests missing required fields fails. */
@@ -75,7 +87,7 @@
public void testMissingRequiredFields() throws Exception {
System.out.println("Starting testMissingRequiredFields");
for (String reqField : REQUIRED_FIELD_NAMES) {
- System.out.println("testing missing required field: " + reqField);
+ System.out.println("testing missing required field hr: " + reqField);
var appInfoEle =
TestUtils.getElementsFromResource(
Paths.get(APP_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
@@ -85,6 +97,17 @@
MalformedXmlException.class,
() -> new AppInfoFactory().createFromHrElements(appInfoEle));
}
+
+ for (String reqField : REQUIRED_FIELD_NAMES_OD) {
+ System.out.println("testing missing required field od: " + reqField);
+ var appInfoEle =
+ TestUtils.getElementsFromResource(
+ Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ TestUtils.removeOdChildEleWithName(appInfoEle.get(0), reqField);
+ assertThrows(
+ MalformedXmlException.class,
+ () -> new AppInfoFactory().createFromOdElements(appInfoEle));
+ }
}
/** Tests missing optional fields passes. */
@@ -96,12 +119,34 @@
Paths.get(APP_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
ele.get(0).removeAttribute(optField);
AppInfo appInfo = new AppInfoFactory().createFromHrElements(ele);
- appInfo.toOdDomElements(mDoc);
+ appInfo.toOdDomElements(TestUtils.document());
+ }
+
+ for (String optField : OPTIONAL_FIELD_NAMES_OD) {
+ var ele =
+ TestUtils.getElementsFromResource(
+ Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ TestUtils.removeOdChildEleWithName(ele.get(0), optField);
+ AppInfo appInfo = new AppInfoFactory().createFromOdElements(ele);
+ appInfo.toHrDomElements(TestUtils.document());
}
}
private void testHrToOdAppInfo(String fileName) throws Exception {
TestUtils.testHrToOd(
- mDoc, new AppInfoFactory(), APP_INFO_HR_PATH, APP_INFO_OD_PATH, fileName);
+ TestUtils.document(),
+ new AppInfoFactory(),
+ APP_INFO_HR_PATH,
+ APP_INFO_OD_PATH,
+ fileName);
+ }
+
+ private void testOdToHrAppInfo(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new AppInfoFactory(),
+ APP_INFO_OD_PATH,
+ APP_INFO_HR_PATH,
+ fileName);
}
}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataCategoryTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataCategoryTest.java
index 822f175..ebb3186 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataCategoryTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataCategoryTest.java
@@ -22,7 +22,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.w3c.dom.Document;
@RunWith(JUnit4.class)
public class DataCategoryTest {
@@ -57,15 +56,12 @@
"data-category-personal-unrecognized-type.xml";
private static final String UNRECOGNIZED_CATEGORY_FILE_NAME = "data-category-unrecognized.xml";
- private Document mDoc = null;
-
/** Logic for setting up tests (empty if not yet needed). */
public static void main(String[] params) throws Exception {}
@Before
public void setUp() throws Exception {
System.out.println("set up.");
- mDoc = TestUtils.document();
}
/** Test for data category personal. */
@@ -207,7 +203,7 @@
private void testHrToOdDataCategory(String fileName) throws Exception {
TestUtils.testHrToOd(
- mDoc,
+ TestUtils.document(),
new DataCategoryFactory(),
DATA_CATEGORY_HR_PATH,
DATA_CATEGORY_OD_PATH,
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java
index 2be447e..2661726 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java
@@ -22,7 +22,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.w3c.dom.Document;
@RunWith(JUnit4.class)
public class DataLabelsTest {
@@ -41,12 +40,33 @@
private static final String SHARED_INVALID_BOOL_FILE_NAME =
"data-labels-shared-invalid-bool.xml";
- private Document mDoc = null;
+ private static final String ACTIONS_IN_APP_FILE_NAME = "data-category-actions-in-app.xml";
+ private static final String APP_PERFORMANCE_FILE_NAME = "data-category-app-performance.xml";
+ private static final String AUDIO_FILE_NAME = "data-category-audio.xml";
+ private static final String CALENDAR_FILE_NAME = "data-category-calendar.xml";
+ private static final String CONTACTS_FILE_NAME = "data-category-contacts.xml";
+ private static final String EMAIL_TEXT_MESSAGE_FILE_NAME =
+ "data-category-email-text-message.xml";
+ private static final String FINANCIAL_FILE_NAME = "data-category-financial.xml";
+ private static final String HEALTH_FITNESS_FILE_NAME = "data-category-health-fitness.xml";
+ private static final String IDENTIFIERS_FILE_NAME = "data-category-identifiers.xml";
+ private static final String LOCATION_FILE_NAME = "data-category-location.xml";
+ private static final String PERSONAL_FILE_NAME = "data-category-personal.xml";
+ private static final String PERSONAL_PARTIAL_FILE_NAME = "data-category-personal-partial.xml";
+ private static final String PHOTO_VIDEO_FILE_NAME = "data-category-photo-video.xml";
+ private static final String SEARCH_AND_BROWSING_FILE_NAME =
+ "data-category-search-and-browsing.xml";
+ private static final String STORAGE_FILE_NAME = "data-category-storage.xml";
+ private static final String PERSONAL_MISSING_PURPOSE_FILE_NAME =
+ "data-category-personal-missing-purpose.xml";
+ private static final String PERSONAL_EMPTY_PURPOSE_FILE_NAME =
+ "data-category-personal-empty-purpose.xml";
+ private static final String UNRECOGNIZED_FILE_NAME = "data-category-unrecognized.xml";
+ private static final String UNRECOGNIZED_TYPE_FILE_NAME = "data-category-unrecognized-type.xml";
@Before
public void setUp() throws Exception {
System.out.println("set up.");
- mDoc = TestUtils.document();
}
/** Test for data labels accessed valid bool. */
@@ -54,6 +74,7 @@
public void testDataLabelsAccessedValidBool() throws Exception {
System.out.println("starting testDataLabelsAccessedValidBool.");
testHrToOdDataLabels(ACCESSED_VALID_BOOL_FILE_NAME);
+ testOdToHrDataLabels(ACCESSED_VALID_BOOL_FILE_NAME);
}
/** Test for data labels accessed invalid bool. */
@@ -68,6 +89,7 @@
public void testDataLabelsCollectedValidBool() throws Exception {
System.out.println("starting testDataLabelsCollectedValidBool.");
testHrToOdDataLabels(COLLECTED_VALID_BOOL_FILE_NAME);
+ testOdToHrDataLabels(COLLECTED_VALID_BOOL_FILE_NAME);
}
/** Test for data labels collected invalid bool. */
@@ -75,6 +97,7 @@
public void testDataLabelsCollectedInvalidBool() throws Exception {
System.out.println("starting testDataLabelsCollectedInvalidBool.");
hrToOdExpectException(COLLECTED_INVALID_BOOL_FILE_NAME);
+ odToHrExpectException(COLLECTED_INVALID_BOOL_FILE_NAME);
}
/** Test for data labels shared valid bool. */
@@ -82,6 +105,7 @@
public void testDataLabelsSharedValidBool() throws Exception {
System.out.println("starting testDataLabelsSharedValidBool.");
testHrToOdDataLabels(SHARED_VALID_BOOL_FILE_NAME);
+ testOdToHrDataLabels(SHARED_VALID_BOOL_FILE_NAME);
}
/** Test for data labels shared invalid bool. */
@@ -91,12 +115,207 @@
hrToOdExpectException(SHARED_INVALID_BOOL_FILE_NAME);
}
+ /* Data categories bidirectional tests... */
+
+ /** Test for data labels actions in app. */
+ @Test
+ public void testDataLabelsActionsInApp() throws Exception {
+ System.out.println("starting testDataLabelsActionsInApp.");
+ testHrToOdDataLabels(ACTIONS_IN_APP_FILE_NAME);
+ testOdToHrDataLabels(ACTIONS_IN_APP_FILE_NAME);
+ }
+
+ /** Test for data labels app performance. */
+ @Test
+ public void testDataLabelsAppPerformance() throws Exception {
+ System.out.println("starting testDataLabelsAppPerformance.");
+ testHrToOdDataLabels(APP_PERFORMANCE_FILE_NAME);
+ testOdToHrDataLabels(APP_PERFORMANCE_FILE_NAME);
+ }
+
+ /** Test for data labels audio. */
+ @Test
+ public void testDataLabelsAudio() throws Exception {
+ System.out.println("starting testDataLabelsAudio.");
+ testHrToOdDataLabels(AUDIO_FILE_NAME);
+ testOdToHrDataLabels(AUDIO_FILE_NAME);
+ }
+
+ /** Test for data labels calendar. */
+ @Test
+ public void testDataLabelsCalendar() throws Exception {
+ System.out.println("starting testDataLabelsCalendar.");
+ testHrToOdDataLabels(CALENDAR_FILE_NAME);
+ testOdToHrDataLabels(CALENDAR_FILE_NAME);
+ }
+
+ /** Test for data labels contacts. */
+ @Test
+ public void testDataLabelsContacts() throws Exception {
+ System.out.println("starting testDataLabelsContacts.");
+ testHrToOdDataLabels(CONTACTS_FILE_NAME);
+ testOdToHrDataLabels(CONTACTS_FILE_NAME);
+ }
+
+ /** Test for data labels email text message. */
+ @Test
+ public void testDataLabelsEmailTextMessage() throws Exception {
+ System.out.println("starting testDataLabelsEmailTextMessage.");
+ testHrToOdDataLabels(EMAIL_TEXT_MESSAGE_FILE_NAME);
+ testOdToHrDataLabels(EMAIL_TEXT_MESSAGE_FILE_NAME);
+ }
+
+ /** Test for data labels financial. */
+ @Test
+ public void testDataLabelsFinancial() throws Exception {
+ System.out.println("starting testDataLabelsFinancial.");
+ testHrToOdDataLabels(FINANCIAL_FILE_NAME);
+ testOdToHrDataLabels(FINANCIAL_FILE_NAME);
+ }
+
+ /** Test for data labels health fitness. */
+ @Test
+ public void testDataLabelsHealthFitness() throws Exception {
+ System.out.println("starting testDataLabelsHealthFitness.");
+ testHrToOdDataLabels(HEALTH_FITNESS_FILE_NAME);
+ testOdToHrDataLabels(HEALTH_FITNESS_FILE_NAME);
+ }
+
+ /** Test for data labels identifiers. */
+ @Test
+ public void testDataLabelsIdentifiers() throws Exception {
+ System.out.println("starting testDataLabelsIdentifiers.");
+ testHrToOdDataLabels(IDENTIFIERS_FILE_NAME);
+ testOdToHrDataLabels(IDENTIFIERS_FILE_NAME);
+ }
+
+ /** Test for data labels location. */
+ @Test
+ public void testDataLabelsLocation() throws Exception {
+ System.out.println("starting testDataLabelsLocation.");
+ testHrToOdDataLabels(LOCATION_FILE_NAME);
+ testOdToHrDataLabels(LOCATION_FILE_NAME);
+ }
+
+ /** Test for data labels personal. */
+ @Test
+ public void testDataLabelsPersonal() throws Exception {
+ System.out.println("starting testDataLabelsPersonal.");
+ testHrToOdDataLabels(PERSONAL_FILE_NAME);
+ testOdToHrDataLabels(PERSONAL_FILE_NAME);
+ }
+
+ /** Test for data labels personal partial. */
+ @Test
+ public void testDataLabelsPersonalPartial() throws Exception {
+ System.out.println("starting testDataLabelsPersonalPartial.");
+ testHrToOdDataLabels(PERSONAL_PARTIAL_FILE_NAME);
+ testOdToHrDataLabels(PERSONAL_PARTIAL_FILE_NAME);
+ }
+
+ /** Test for data labels photo video. */
+ @Test
+ public void testDataLabelsPhotoVideo() throws Exception {
+ System.out.println("starting testDataLabelsPhotoVideo.");
+ testHrToOdDataLabels(PHOTO_VIDEO_FILE_NAME);
+ testOdToHrDataLabels(PHOTO_VIDEO_FILE_NAME);
+ }
+
+ /** Test for data labels search and browsing. */
+ @Test
+ public void testDataLabelsSearchAndBrowsing() throws Exception {
+ System.out.println("starting testDataLabelsSearchAndBrowsing.");
+ testHrToOdDataLabels(SEARCH_AND_BROWSING_FILE_NAME);
+ testOdToHrDataLabels(SEARCH_AND_BROWSING_FILE_NAME);
+ }
+
+ /** Test for data labels storage. */
+ @Test
+ public void testDataLabelsStorage() throws Exception {
+ System.out.println("starting testDataLabelsStorage.");
+ testHrToOdDataLabels(STORAGE_FILE_NAME);
+ testOdToHrDataLabels(STORAGE_FILE_NAME);
+ }
+
+ /** Test for data labels hr unrecognized data category. */
+ @Test
+ public void testDataLabelsHrUnrecognizedDataCategory() throws Exception {
+ System.out.println("starting testDataLabelsHrUnrecognizedDataCategory.");
+ hrToOdExpectException(UNRECOGNIZED_FILE_NAME);
+ }
+
+ /** Test for data labels hr unrecognized data type. */
+ @Test
+ public void testDataLabelsHrUnrecognizedDataType() throws Exception {
+ System.out.println("starting testDataLabelsHrUnrecognizedDataType.");
+ hrToOdExpectException(UNRECOGNIZED_TYPE_FILE_NAME);
+ }
+
+ /** Test for data labels hr missing purpose. */
+ @Test
+ public void testDataLabelsHrMissingPurpose() throws Exception {
+ System.out.println("starting testDataLabelsHrMissingPurpose.");
+ hrToOdExpectException(PERSONAL_MISSING_PURPOSE_FILE_NAME);
+ }
+
+ /** Test for data labels hr empty purpose. */
+ @Test
+ public void testDataLabelsHrEmptyPurpose() throws Exception {
+ System.out.println("starting testDataLabelsHrEmptyPurpose.");
+ hrToOdExpectException(PERSONAL_EMPTY_PURPOSE_FILE_NAME);
+ }
+
+ /** Test for data labels od unrecognized data category. */
+ @Test
+ public void testDataLabelsOdUnrecognizedDataCategory() throws Exception {
+ System.out.println("starting testDataLabelsOdUnrecognizedDataCategory.");
+ odToHrExpectException(UNRECOGNIZED_FILE_NAME);
+ }
+
+ /** Test for data labels od unrecognized data type. */
+ @Test
+ public void testDataLabelsOdUnrecognizedDataType() throws Exception {
+ System.out.println("starting testDataLabelsOdUnrecognizedDataCategory.");
+ odToHrExpectException(UNRECOGNIZED_TYPE_FILE_NAME);
+ }
+
+ /** Test for data labels od missing purpose. */
+ @Test
+ public void testDataLabelsOdMissingPurpose() throws Exception {
+ System.out.println("starting testDataLabelsOdMissingPurpose.");
+ odToHrExpectException(PERSONAL_MISSING_PURPOSE_FILE_NAME);
+ }
+
+ /** Test for data labels od empty purpose. */
+ @Test
+ public void testDataLabelsOdEmptyPurpose() throws Exception {
+ System.out.println("starting testDataLabelsOdEmptyPurpose.");
+ odToHrExpectException(PERSONAL_EMPTY_PURPOSE_FILE_NAME);
+ }
+
private void hrToOdExpectException(String fileName) {
TestUtils.hrToOdExpectException(new DataLabelsFactory(), DATA_LABELS_HR_PATH, fileName);
}
+ private void odToHrExpectException(String fileName) {
+ TestUtils.odToHrExpectException(new DataLabelsFactory(), DATA_LABELS_OD_PATH, fileName);
+ }
+
private void testHrToOdDataLabels(String fileName) throws Exception {
TestUtils.testHrToOd(
- mDoc, new DataLabelsFactory(), DATA_LABELS_HR_PATH, DATA_LABELS_OD_PATH, fileName);
+ TestUtils.document(),
+ new DataLabelsFactory(),
+ DATA_LABELS_HR_PATH,
+ DATA_LABELS_OD_PATH,
+ fileName);
+ }
+
+ private void testOdToHrDataLabels(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new DataLabelsFactory(),
+ DATA_LABELS_OD_PATH,
+ DATA_LABELS_HR_PATH,
+ fileName);
}
}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java
index ff8346a..72e8d65 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java
@@ -25,7 +25,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.w3c.dom.Document;
import java.nio.file.Paths;
import java.util.List;
@@ -36,19 +35,20 @@
private static final String DEVELOPER_INFO_OD_PATH = "com/android/asllib/developerinfo/od";
public static final List<String> REQUIRED_FIELD_NAMES =
List.of("address", "countryRegion", "email", "name", "relationship");
+ public static final List<String> REQUIRED_FIELD_NAMES_OD =
+ List.of("address", "country_region", "email", "name", "relationship");
public static final List<String> OPTIONAL_FIELD_NAMES = List.of("website", "registryId");
+ public static final List<String> OPTIONAL_FIELD_NAMES_OD =
+ List.of("website", "app_developer_registry_id");
private static final String ALL_FIELDS_VALID_FILE_NAME = "all-fields-valid.xml";
- private Document mDoc = null;
-
/** Logic for setting up tests (empty if not yet needed). */
public static void main(String[] params) throws Exception {}
@Before
public void setUp() throws Exception {
System.out.println("set up.");
- mDoc = TestUtils.document();
}
/** Test for all fields valid. */
@@ -56,6 +56,7 @@
public void testAllFieldsValid() throws Exception {
System.out.println("starting testAllFieldsValid.");
testHrToOdDeveloperInfo(ALL_FIELDS_VALID_FILE_NAME);
+ testOdToHrDeveloperInfo(ALL_FIELDS_VALID_FILE_NAME);
}
/** Tests missing required fields fails. */
@@ -73,6 +74,18 @@
MalformedXmlException.class,
() -> new DeveloperInfoFactory().createFromHrElements(developerInfoEle));
}
+
+ for (String reqField : REQUIRED_FIELD_NAMES_OD) {
+ System.out.println("testing missing required field od: " + reqField);
+ var developerInfoEle =
+ TestUtils.getElementsFromResource(
+ Paths.get(DEVELOPER_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ TestUtils.removeOdChildEleWithName(developerInfoEle.get(0), reqField);
+
+ assertThrows(
+ MalformedXmlException.class,
+ () -> new DeveloperInfoFactory().createFromOdElements(developerInfoEle));
+ }
}
/** Tests missing optional fields passes. */
@@ -85,16 +98,35 @@
developerInfoEle.get(0).removeAttribute(optField);
DeveloperInfo developerInfo =
new DeveloperInfoFactory().createFromHrElements(developerInfoEle);
- developerInfo.toOdDomElements(mDoc);
+ developerInfo.toOdDomElements(TestUtils.document());
+ }
+
+ for (String optField : OPTIONAL_FIELD_NAMES_OD) {
+ var developerInfoEle =
+ TestUtils.getElementsFromResource(
+ Paths.get(DEVELOPER_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ TestUtils.removeOdChildEleWithName(developerInfoEle.get(0), optField);
+ DeveloperInfo developerInfo =
+ new DeveloperInfoFactory().createFromOdElements(developerInfoEle);
+ developerInfo.toHrDomElements(TestUtils.document());
}
}
private void testHrToOdDeveloperInfo(String fileName) throws Exception {
TestUtils.testHrToOd(
- mDoc,
+ TestUtils.document(),
new DeveloperInfoFactory(),
DEVELOPER_INFO_HR_PATH,
DEVELOPER_INFO_OD_PATH,
fileName);
}
+
+ private void testOdToHrDeveloperInfo(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new DeveloperInfoFactory(),
+ DEVELOPER_INFO_OD_PATH,
+ DEVELOPER_INFO_HR_PATH,
+ fileName);
+ }
}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java
index c52d6c8..bba6b54 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java
@@ -22,7 +22,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.w3c.dom.Document;
@RunWith(JUnit4.class)
public class SafetyLabelsTest {
@@ -36,12 +35,9 @@
private static final String WITH_THIRD_PARTY_VERIFICATION_FILE_NAME =
"with-third-party-verification.xml";
- private Document mDoc = null;
-
@Before
public void setUp() throws Exception {
System.out.println("set up.");
- mDoc = TestUtils.document();
}
/** Test for safety labels missing version. */
@@ -49,6 +45,7 @@
public void testSafetyLabelsMissingVersion() throws Exception {
System.out.println("starting testSafetyLabelsMissingVersion.");
hrToOdExpectException(MISSING_VERSION_FILE_NAME);
+ odToHrExpectException(MISSING_VERSION_FILE_NAME);
}
/** Test for safety labels valid empty. */
@@ -56,6 +53,7 @@
public void testSafetyLabelsValidEmptyFile() throws Exception {
System.out.println("starting testSafetyLabelsValidEmptyFile.");
testHrToOdSafetyLabels(VALID_EMPTY_FILE_NAME);
+ testOdToHrSafetyLabels(VALID_EMPTY_FILE_NAME);
}
/** Test for safety labels with data labels. */
@@ -63,6 +61,7 @@
public void testSafetyLabelsWithDataLabels() throws Exception {
System.out.println("starting testSafetyLabelsWithDataLabels.");
testHrToOdSafetyLabels(WITH_DATA_LABELS_FILE_NAME);
+ testOdToHrSafetyLabels(WITH_DATA_LABELS_FILE_NAME);
}
/** Test for safety labels with security labels. */
@@ -70,6 +69,7 @@
public void testSafetyLabelsWithSecurityLabels() throws Exception {
System.out.println("starting testSafetyLabelsWithSecurityLabels.");
testHrToOdSafetyLabels(WITH_SECURITY_LABELS_FILE_NAME);
+ testOdToHrSafetyLabels(WITH_SECURITY_LABELS_FILE_NAME);
}
/** Test for safety labels with third party verification. */
@@ -77,18 +77,32 @@
public void testSafetyLabelsWithThirdPartyVerification() throws Exception {
System.out.println("starting testSafetyLabelsWithThirdPartyVerification.");
testHrToOdSafetyLabels(WITH_THIRD_PARTY_VERIFICATION_FILE_NAME);
+ testOdToHrSafetyLabels(WITH_THIRD_PARTY_VERIFICATION_FILE_NAME);
}
private void hrToOdExpectException(String fileName) {
TestUtils.hrToOdExpectException(new SafetyLabelsFactory(), SAFETY_LABELS_HR_PATH, fileName);
}
+ private void odToHrExpectException(String fileName) {
+ TestUtils.odToHrExpectException(new SafetyLabelsFactory(), SAFETY_LABELS_OD_PATH, fileName);
+ }
+
private void testHrToOdSafetyLabels(String fileName) throws Exception {
TestUtils.testHrToOd(
- mDoc,
+ TestUtils.document(),
new SafetyLabelsFactory(),
SAFETY_LABELS_HR_PATH,
SAFETY_LABELS_OD_PATH,
fileName);
}
+
+ private void testOdToHrSafetyLabels(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new SafetyLabelsFactory(),
+ SAFETY_LABELS_OD_PATH,
+ SAFETY_LABELS_HR_PATH,
+ fileName);
+ }
}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java
index c0d0d72..a940bc6 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java
@@ -23,7 +23,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.w3c.dom.Document;
import java.nio.file.Paths;
import java.util.List;
@@ -35,18 +34,17 @@
public static final List<String> OPTIONAL_FIELD_NAMES =
List.of("isDataDeletable", "isDataEncrypted");
+ public static final List<String> OPTIONAL_FIELD_NAMES_OD =
+ List.of("is_data_deletable", "is_data_encrypted");
private static final String ALL_FIELDS_VALID_FILE_NAME = "all-fields-valid.xml";
- private Document mDoc = null;
-
/** Logic for setting up tests (empty if not yet needed). */
public static void main(String[] params) throws Exception {}
@Before
public void setUp() throws Exception {
System.out.println("set up.");
- mDoc = TestUtils.document();
}
/** Test for all fields valid. */
@@ -54,6 +52,7 @@
public void testAllFieldsValid() throws Exception {
System.out.println("starting testAllFieldsValid.");
testHrToOdSecurityLabels(ALL_FIELDS_VALID_FILE_NAME);
+ testOdToHrSecurityLabels(ALL_FIELDS_VALID_FILE_NAME);
}
/** Tests missing optional fields passes. */
@@ -65,16 +64,33 @@
Paths.get(SECURITY_LABELS_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
ele.get(0).removeAttribute(optField);
SecurityLabels securityLabels = new SecurityLabelsFactory().createFromHrElements(ele);
- securityLabels.toOdDomElements(mDoc);
+ securityLabels.toOdDomElements(TestUtils.document());
+ }
+ for (String optField : OPTIONAL_FIELD_NAMES_OD) {
+ var ele =
+ TestUtils.getElementsFromResource(
+ Paths.get(SECURITY_LABELS_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ TestUtils.removeOdChildEleWithName(ele.get(0), optField);
+ SecurityLabels securityLabels = new SecurityLabelsFactory().createFromOdElements(ele);
+ securityLabels.toHrDomElements(TestUtils.document());
}
}
private void testHrToOdSecurityLabels(String fileName) throws Exception {
TestUtils.testHrToOd(
- mDoc,
+ TestUtils.document(),
new SecurityLabelsFactory(),
SECURITY_LABELS_HR_PATH,
SECURITY_LABELS_OD_PATH,
fileName);
}
+
+ private void testOdToHrSecurityLabels(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new SecurityLabelsFactory(),
+ SECURITY_LABELS_OD_PATH,
+ SECURITY_LABELS_HR_PATH,
+ fileName);
+ }
}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java
index 191091a..33c2764 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java
@@ -22,7 +22,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.w3c.dom.Document;
@RunWith(JUnit4.class)
public class SystemAppSafetyLabelTest {
@@ -34,15 +33,12 @@
private static final String VALID_FILE_NAME = "valid.xml";
private static final String MISSING_URL_FILE_NAME = "missing-url.xml";
- private Document mDoc = null;
-
/** Logic for setting up tests (empty if not yet needed). */
public static void main(String[] params) throws Exception {}
@Before
public void setUp() throws Exception {
System.out.println("set up.");
- mDoc = TestUtils.document();
}
/** Test for valid. */
@@ -50,6 +46,7 @@
public void testValid() throws Exception {
System.out.println("starting testValid.");
testHrToOdSystemAppSafetyLabel(VALID_FILE_NAME);
+ testOdToHrSystemAppSafetyLabel(VALID_FILE_NAME);
}
/** Tests missing url. */
@@ -57,6 +54,7 @@
public void testMissingUrl() throws Exception {
System.out.println("starting testMissingUrl.");
hrToOdExpectException(MISSING_URL_FILE_NAME);
+ odToHrExpectException(MISSING_URL_FILE_NAME);
}
private void hrToOdExpectException(String fileName) {
@@ -64,12 +62,26 @@
new SystemAppSafetyLabelFactory(), SYSTEM_APP_SAFETY_LABEL_HR_PATH, fileName);
}
+ private void odToHrExpectException(String fileName) {
+ TestUtils.odToHrExpectException(
+ new SystemAppSafetyLabelFactory(), SYSTEM_APP_SAFETY_LABEL_OD_PATH, fileName);
+ }
+
private void testHrToOdSystemAppSafetyLabel(String fileName) throws Exception {
TestUtils.testHrToOd(
- mDoc,
+ TestUtils.document(),
new SystemAppSafetyLabelFactory(),
SYSTEM_APP_SAFETY_LABEL_HR_PATH,
SYSTEM_APP_SAFETY_LABEL_OD_PATH,
fileName);
}
+
+ private void testOdToHrSystemAppSafetyLabel(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new SystemAppSafetyLabelFactory(),
+ SYSTEM_APP_SAFETY_LABEL_OD_PATH,
+ SYSTEM_APP_SAFETY_LABEL_HR_PATH,
+ fileName);
+ }
}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java
index ab8e85c..ec86d0f 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java
@@ -22,7 +22,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.w3c.dom.Document;
@RunWith(JUnit4.class)
public class ThirdPartyVerificationTest {
@@ -34,15 +33,12 @@
private static final String VALID_FILE_NAME = "valid.xml";
private static final String MISSING_URL_FILE_NAME = "missing-url.xml";
- private Document mDoc = null;
-
/** Logic for setting up tests (empty if not yet needed). */
public static void main(String[] params) throws Exception {}
@Before
public void setUp() throws Exception {
System.out.println("set up.");
- mDoc = TestUtils.document();
}
/** Test for valid. */
@@ -50,6 +46,7 @@
public void testValid() throws Exception {
System.out.println("starting testValid.");
testHrToOdThirdPartyVerification(VALID_FILE_NAME);
+ testOdToHrThirdPartyVerification(VALID_FILE_NAME);
}
/** Tests missing url. */
@@ -57,6 +54,7 @@
public void testMissingUrl() throws Exception {
System.out.println("starting testMissingUrl.");
hrToOdExpectException(MISSING_URL_FILE_NAME);
+ odToHrExpectException(MISSING_URL_FILE_NAME);
}
private void hrToOdExpectException(String fileName) {
@@ -64,12 +62,26 @@
new ThirdPartyVerificationFactory(), THIRD_PARTY_VERIFICATION_HR_PATH, fileName);
}
+ private void odToHrExpectException(String fileName) {
+ TestUtils.odToHrExpectException(
+ new ThirdPartyVerificationFactory(), THIRD_PARTY_VERIFICATION_OD_PATH, fileName);
+ }
+
private void testHrToOdThirdPartyVerification(String fileName) throws Exception {
TestUtils.testHrToOd(
- mDoc,
+ TestUtils.document(),
new ThirdPartyVerificationFactory(),
THIRD_PARTY_VERIFICATION_HR_PATH,
THIRD_PARTY_VERIFICATION_OD_PATH,
fileName);
}
+
+ private void testOdToHrThirdPartyVerification(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new ThirdPartyVerificationFactory(),
+ THIRD_PARTY_VERIFICATION_OD_PATH,
+ THIRD_PARTY_VERIFICATION_HR_PATH,
+ fileName);
+ }
}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java
index 56503f7..f494240 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java
@@ -22,7 +22,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.w3c.dom.Document;
@RunWith(JUnit4.class)
public class TransparencyInfoTest {
@@ -35,12 +34,9 @@
private static final String WITH_DEVELOPER_INFO_FILE_NAME = "with-developer-info.xml";
private static final String WITH_APP_INFO_FILE_NAME = "with-app-info.xml";
- private Document mDoc = null;
-
@Before
public void setUp() throws Exception {
System.out.println("set up.");
- mDoc = TestUtils.document();
}
/** Test for transparency info valid empty. */
@@ -48,6 +44,7 @@
public void testTransparencyInfoValidEmptyFile() throws Exception {
System.out.println("starting testTransparencyInfoValidEmptyFile.");
testHrToOdTransparencyInfo(VALID_EMPTY_FILE_NAME);
+ testOdToHrTransparencyInfo(VALID_EMPTY_FILE_NAME);
}
/** Test for transparency info with developer info. */
@@ -55,6 +52,7 @@
public void testTransparencyInfoWithDeveloperInfo() throws Exception {
System.out.println("starting testTransparencyInfoWithDeveloperInfo.");
testHrToOdTransparencyInfo(WITH_DEVELOPER_INFO_FILE_NAME);
+ testOdToHrTransparencyInfo(WITH_DEVELOPER_INFO_FILE_NAME);
}
/** Test for transparency info with app info. */
@@ -62,14 +60,24 @@
public void testTransparencyInfoWithAppInfo() throws Exception {
System.out.println("starting testTransparencyInfoWithAppInfo.");
testHrToOdTransparencyInfo(WITH_APP_INFO_FILE_NAME);
+ testOdToHrTransparencyInfo(WITH_APP_INFO_FILE_NAME);
}
private void testHrToOdTransparencyInfo(String fileName) throws Exception {
TestUtils.testHrToOd(
- mDoc,
+ TestUtils.document(),
new TransparencyInfoFactory(),
TRANSPARENCY_INFO_HR_PATH,
TRANSPARENCY_INFO_OD_PATH,
fileName);
}
+
+ private void testOdToHrTransparencyInfo(String fileName) throws Exception {
+ TestUtils.testOdToHr(
+ TestUtils.document(),
+ new TransparencyInfoFactory(),
+ TRANSPARENCY_INFO_OD_PATH,
+ TRANSPARENCY_INFO_HR_PATH,
+ fileName);
+ }
}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/testutils/TestUtils.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/testutils/TestUtils.java
index faea340..ea90993 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/testutils/TestUtils.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/testutils/TestUtils.java
@@ -26,6 +26,8 @@
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.ByteArrayInputStream;
@@ -36,6 +38,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
+import java.util.Optional;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -72,7 +75,7 @@
.findFirst()
.get()
.getTagName();
- return XmlUtils.asElementList(root.getElementsByTagName(tagName));
+ return XmlUtils.getChildrenByTagName(root, tagName);
} else {
return List.of(root);
}
@@ -96,6 +99,19 @@
return outStream.toString(StandardCharsets.UTF_8);
}
+ /** Removes on-device style child with the corresponding name */
+ public static void removeOdChildEleWithName(Element ele, String childNameName) {
+ Optional<Element> childEle =
+ XmlUtils.asElementList(ele.getChildNodes()).stream()
+ .filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(childNameName))
+ .findFirst();
+ if (childEle.isEmpty()) {
+ throw new IllegalStateException(
+ String.format("%s was not found in %s", childNameName, ele.getTagName()));
+ }
+ ele.removeChild(childEle.get());
+ }
+
/**
* Gets formatted XML for slightly more robust comparison checking than naive string comparison.
*/
@@ -105,7 +121,7 @@
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
Document document = factory.newDocumentBuilder().parse(stream);
-
+ stripEmptyElements(document);
return docToStr(document, omitXmlDeclaration);
}
@@ -125,6 +141,17 @@
});
}
+ /** Helper for testing on-device to human-readable conversion expecting exception */
+ public static <T extends AslMarshallable> void odToHrExpectException(
+ AslMarshallableFactory<T> factory, String odFolderPath, String fileName) {
+ assertThrows(
+ MalformedXmlException.class,
+ () -> {
+ factory.createFromOdElements(
+ TestUtils.getElementsFromResource(Paths.get(odFolderPath, fileName)));
+ });
+ }
+
/** Helper for testing human-readable to on-device conversion */
public static <T extends AslMarshallable> void testHrToOd(
Document doc,
@@ -133,20 +160,71 @@
String odFolderPath,
String fileName)
throws Exception {
+ testFormatToFormat(doc, factory, hrFolderPath, odFolderPath, fileName, true);
+ }
+
+ /** Helper for testing on-device to human-readable conversion */
+ public static <T extends AslMarshallable> void testOdToHr(
+ Document doc,
+ AslMarshallableFactory<T> factory,
+ String odFolderPath,
+ String hrFolderPath,
+ String fileName)
+ throws Exception {
+ testFormatToFormat(doc, factory, odFolderPath, hrFolderPath, fileName, false);
+ }
+
+ /** Helper for testing format to format conversion */
+ private static <T extends AslMarshallable> void testFormatToFormat(
+ Document doc,
+ AslMarshallableFactory<T> factory,
+ String inFolderPath,
+ String outFolderPath,
+ String fileName,
+ boolean hrToOd)
+ throws Exception {
AslMarshallable marshallable =
- factory.createFromHrElements(
- TestUtils.getElementsFromResource(Paths.get(hrFolderPath, fileName)));
+ hrToOd
+ ? factory.createFromHrElements(
+ TestUtils.getElementsFromResource(
+ Paths.get(inFolderPath, fileName)))
+ : factory.createFromOdElements(
+ TestUtils.getElementsFromResource(
+ Paths.get(inFolderPath, fileName)));
- for (var child : marshallable.toOdDomElements(doc)) {
- doc.appendChild(child);
+ List<Element> elements =
+ hrToOd ? marshallable.toOdDomElements(doc) : marshallable.toHrDomElements(doc);
+ if (elements.isEmpty()) {
+ throw new IllegalStateException("elements was empty.");
+ } else if (elements.size() == 1) {
+ doc.appendChild(elements.get(0));
+ } else {
+ Element root = doc.createElement(TestUtils.HOLDER_TAG_NAME);
+ for (var child : elements) {
+ root.appendChild(child);
+ }
+ doc.appendChild(root);
}
- String converted = TestUtils.docToStr(doc, true);
- System.out.println("converted: " + converted);
+ String converted = TestUtils.getFormattedXml(TestUtils.docToStr(doc, true), true);
+ System.out.println("Converted: " + converted);
+ String expectedOutContents =
+ TestUtils.getFormattedXml(
+ TestUtils.readStrFromResource(Paths.get(outFolderPath, fileName)), true);
+ System.out.println("Expected: " + expectedOutContents);
+ assertEquals(expectedOutContents, converted);
+ }
- String expectedOdContents =
- TestUtils.readStrFromResource(Paths.get(odFolderPath, fileName));
- assertEquals(
- TestUtils.getFormattedXml(expectedOdContents, true),
- TestUtils.getFormattedXml(converted, true));
+ private static void stripEmptyElements(Node node) {
+ NodeList children = node.getChildNodes();
+ for (int i = 0; i < children.getLength(); ++i) {
+ Node child = children.item(i);
+ if (child.getNodeType() == Node.TEXT_NODE) {
+ if (child.getTextContent().trim().length() == 0) {
+ child.getParentNode().removeChild(child);
+ i--;
+ }
+ }
+ stripEmptyElements(child);
+ }
}
}
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/missing-version.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/missing-version.xml
new file mode 100644
index 0000000..1aa3aa9
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/missing-version.xml
@@ -0,0 +1,2 @@
+<bundle>
+</bundle>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml
new file mode 100644
index 0000000..68e191e
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml
@@ -0,0 +1,17 @@
+<data-labels>
+ <data-shared dataCategory="actions_in_app"
+ dataType="user_interaction"
+ purposes="analytics" />
+ <data-shared dataCategory="actions_in_app"
+ dataType="in_app_search_history"
+ purposes="analytics" />
+ <data-shared dataCategory="actions_in_app"
+ dataType="installed_apps"
+ purposes="analytics" />
+ <data-shared dataCategory="actions_in_app"
+ dataType="user_generated_content"
+ purposes="analytics" />
+ <data-shared dataCategory="actions_in_app"
+ dataType="other"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml
new file mode 100644
index 0000000..a6bd17d
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml
@@ -0,0 +1,11 @@
+<data-labels>
+ <data-shared dataCategory="app_performance"
+ dataType="crash_logs"
+ purposes="analytics" />
+ <data-shared dataCategory="app_performance"
+ dataType="performance_diagnostics"
+ purposes="analytics" />
+ <data-shared dataCategory="app_performance"
+ dataType="other"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml
new file mode 100644
index 0000000..6274604
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml
@@ -0,0 +1,11 @@
+<data-labels>
+ <data-shared dataCategory="audio"
+ dataType="sound_recordings"
+ purposes="analytics" />
+ <data-shared dataCategory="audio"
+ dataType="music_files"
+ purposes="analytics" />
+ <data-shared dataCategory="audio"
+ dataType="other"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml
new file mode 100644
index 0000000..f7201f6
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-shared dataCategory="calendar"
+ dataType="calendar"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml
new file mode 100644
index 0000000..e8d40be
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-shared dataCategory="contacts"
+ dataType="contacts"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml
new file mode 100644
index 0000000..69e9b87
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml
@@ -0,0 +1,11 @@
+<data-labels>
+ <data-shared dataCategory="email_text_message"
+ dataType="emails"
+ purposes="analytics" />
+ <data-shared dataCategory="email_text_message"
+ dataType="text_messages"
+ purposes="analytics" />
+ <data-shared dataCategory="email_text_message"
+ dataType="other"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml
new file mode 100644
index 0000000..fdd8456
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml
@@ -0,0 +1,14 @@
+<data-labels>
+ <data-shared dataCategory="financial"
+ dataType="card_bank_account"
+ purposes="analytics" />
+ <data-shared dataCategory="financial"
+ dataType="purchase_history"
+ purposes="analytics" />
+ <data-shared dataCategory="financial"
+ dataType="credit_score"
+ purposes="analytics" />
+ <data-shared dataCategory="financial"
+ dataType="other"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml
new file mode 100644
index 0000000..bac58e6
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml
@@ -0,0 +1,8 @@
+<data-labels>
+ <data-shared dataCategory="health_fitness"
+ dataType="health"
+ purposes="analytics" />
+ <data-shared dataCategory="health_fitness"
+ dataType="fitness"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml
new file mode 100644
index 0000000..ee45f26
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-shared dataCategory="identifiers"
+ dataType="other"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml
new file mode 100644
index 0000000..e8e5911
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml
@@ -0,0 +1,8 @@
+<data-labels>
+ <data-shared dataCategory="location"
+ dataType="approx_location"
+ purposes="analytics" />
+ <data-shared dataCategory="location"
+ dataType="precise_location"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml
new file mode 100644
index 0000000..0b220f4
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-shared dataCategory="personal"
+ dataType="email_address"
+ purposes="" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml
new file mode 100644
index 0000000..ac221f2
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml
@@ -0,0 +1,4 @@
+<data-labels>
+ <data-shared dataCategory="personal"
+ dataType="email_address" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml
new file mode 100644
index 0000000..11b7368
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml
@@ -0,0 +1,8 @@
+<data-labels>
+ <data-shared dataCategory="personal"
+ dataType="name"
+ purposes="analytics|developer_communications" />
+ <data-shared dataCategory="personal"
+ dataType="email_address"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml
new file mode 100644
index 0000000..f1fbd56
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-shared dataCategory="personal"
+ dataType="unrecognized"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml
new file mode 100644
index 0000000..5907462
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml
@@ -0,0 +1,31 @@
+<data-labels>
+ <data-shared dataCategory="personal"
+ dataType="name"
+ ephemeral="true"
+ isSharingOptional="true"
+ purposes="analytics|developer_communications" />
+ <data-shared dataCategory="personal"
+ dataType="email_address"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="physical_address"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="phone_number"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="race_ethnicity"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="political_or_religious_beliefs"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="sexual_orientation_or_gender_identity"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="personal_identifiers"
+ purposes="analytics" />
+ <data-shared dataCategory="personal"
+ dataType="other"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml
new file mode 100644
index 0000000..05fe159
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml
@@ -0,0 +1,8 @@
+<data-labels>
+ <data-shared dataCategory="photo_video"
+ dataType="photos"
+ purposes="analytics" />
+ <data-shared dataCategory="photo_video"
+ dataType="videos"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml
new file mode 100644
index 0000000..a5de7be
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-shared dataCategory="search_and_browsing"
+ dataType="web_browsing_history"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml
new file mode 100644
index 0000000..f01e2df
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-shared dataCategory="storage"
+ dataType="files_docs"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml
new file mode 100644
index 0000000..f1fbd56
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-shared dataCategory="personal"
+ dataType="unrecognized"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml
new file mode 100644
index 0000000..c5be684
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml
@@ -0,0 +1,5 @@
+<data-labels>
+ <data-shared dataCategory="unrecognized"
+ dataType="email_address"
+ purposes="analytics" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml
new file mode 100644
index 0000000..161057a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml
@@ -0,0 +1,11 @@
+<data-labels>
+ <data-accessed dataCategory="location"
+ dataType="approx_location"
+ purposes="app_functionality" />
+ <data-collected dataCategory="location"
+ dataType="precise_location"
+ purposes="app_functionality" />
+ <data-shared dataCategory="personal"
+ dataType="name"
+ purposes="app_functionality" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-actions-in-app.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-actions-in-app.xml
new file mode 100644
index 0000000..c5fef58
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-actions-in-app.xml
@@ -0,0 +1,31 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="actions_in_app">
+ <pbundle_as_map name="user_interaction">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="in_app_search_history">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="installed_apps">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="user_generated_content">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-app-performance.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-app-performance.xml
new file mode 100644
index 0000000..4570145
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-app-performance.xml
@@ -0,0 +1,21 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="app_performance">
+ <pbundle_as_map name="crash_logs">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="performance_diagnostics">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-audio.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-audio.xml
new file mode 100644
index 0000000..120f626
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-audio.xml
@@ -0,0 +1,21 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="audio">
+ <pbundle_as_map name="sound_recordings">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="music_files">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-calendar.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-calendar.xml
new file mode 100644
index 0000000..59eb938
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-calendar.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="calendar">
+ <pbundle_as_map name="calendar">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-contacts.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-contacts.xml
new file mode 100644
index 0000000..f952bfb
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-contacts.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="contacts">
+ <pbundle_as_map name="contacts">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-email-text-message.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-email-text-message.xml
new file mode 100644
index 0000000..bcaa716
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-email-text-message.xml
@@ -0,0 +1,21 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="email_text_message">
+ <pbundle_as_map name="emails">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="text_messages">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-financial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-financial.xml
new file mode 100644
index 0000000..a7bc82e
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-financial.xml
@@ -0,0 +1,26 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="financial">
+ <pbundle_as_map name="card_bank_account">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="purchase_history">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="credit_score">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-health-fitness.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-health-fitness.xml
new file mode 100644
index 0000000..f3ab2bd9
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-health-fitness.xml
@@ -0,0 +1,16 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="health_fitness">
+ <pbundle_as_map name="health">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="fitness">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-identifiers.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-identifiers.xml
new file mode 100644
index 0000000..05c07e5
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-identifiers.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="identifiers">
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-location.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-location.xml
new file mode 100644
index 0000000..931d1ad
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-location.xml
@@ -0,0 +1,16 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="precise_location">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-empty-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-empty-purpose.xml
new file mode 100644
index 0000000..83f4a67
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-empty-purpose.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="personal">
+ <pbundle_as_map name="email_address">
+
+ <int-array name="purposes" num="2">
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-missing-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-missing-purpose.xml
new file mode 100644
index 0000000..532f5de
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-missing-purpose.xml
@@ -0,0 +1,8 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="personal">
+ <pbundle_as_map name="email_address">
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml
new file mode 100644
index 0000000..14f9ef2
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml
@@ -0,0 +1,17 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="personal">
+ <pbundle_as_map name="name">
+ <int-array name="purposes" num="2">
+ <item value="2" />
+ <item value="3" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="email_address">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml
new file mode 100644
index 0000000..1c87de9
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml
@@ -0,0 +1,54 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="personal">
+ <pbundle_as_map name="name">
+ <int-array name="purposes" num="2">
+ <item value="2" />
+ <item value="3" />
+ </int-array>
+ <boolean name="is_sharing_optional" value="true" />
+ <boolean name="ephemeral" value="true" />
+ </pbundle_as_map>
+ <pbundle_as_map name="email_address">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="physical_address">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="phone_number">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="race_ethnicity">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="political_or_religious_beliefs">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="sexual_orientation_or_gender_identity">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="personal_identifiers">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="other">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-photo-video.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-photo-video.xml
new file mode 100644
index 0000000..a752b515
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-photo-video.xml
@@ -0,0 +1,16 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="photo_video">
+ <pbundle_as_map name="photos">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="videos">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-search-and-browsing.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-search-and-browsing.xml
new file mode 100644
index 0000000..99bc188
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-search-and-browsing.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="search_and_browsing">
+ <pbundle_as_map name="web_browsing_history">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-storage.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-storage.xml
new file mode 100644
index 0000000..a4d2a62
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-storage.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="storage">
+ <pbundle_as_map name="files_docs">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized-type.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized-type.xml
new file mode 100644
index 0000000..16195df
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized-type.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="personal">
+ <pbundle_as_map name="unrecognized">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized.xml
new file mode 100644
index 0000000..511940e
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-unrecognized.xml
@@ -0,0 +1,11 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="unrecognized">
+ <pbundle_as_map name="email_address">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+</pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-collected-shared.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-collected-shared.xml
new file mode 100644
index 0000000..f86dbc1
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-collected-shared.xml
@@ -0,0 +1,29 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_accessed">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ <pbundle_as_map name="data_collected">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="precise_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ <pbundle_as_map name="data_shared">
+ <pbundle_as_map name="personal">
+ <pbundle_as_map name="name">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-invalid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-invalid-bool.xml
new file mode 100644
index 0000000..54cc8e7
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-invalid-bool.xml
@@ -0,0 +1,13 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_collected">
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_sharing_optional" value="false"/>
+ <boolean name="ephemeral" value="false"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml
index d1d4e33..3864f98 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml
@@ -1,5 +1,5 @@
<pbundle_as_map name="data_labels">
- <pbundle_as_map name="data_shared">
+<pbundle_as_map name="data_shared">
<pbundle_as_map name="location">
<pbundle_as_map name="approx_location">
<int-array name="purposes" num="1">
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/missing-version.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/missing-version.xml
new file mode 100644
index 0000000..3fbe359
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/missing-version.xml
@@ -0,0 +1,2 @@
+<pbundle_as_map name="safety_labels">
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-url.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-url.xml
new file mode 100644
index 0000000..33b7965
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-url.xml
@@ -0,0 +1,2 @@
+<pbundle_as_map name="system_app_safety_label">
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/od/missing-url.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/od/missing-url.xml
new file mode 100644
index 0000000..0b5a46f
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/thirdpartyverification/od/missing-url.xml
@@ -0,0 +1,2 @@
+<pbundle_as_map name="third_party_verification">
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-developer-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-developer-info.xml
index 862bda4..d16caae 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-developer-info.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-developer-info.xml
@@ -7,5 +7,5 @@
countryRegion="US"
relationship="aosp"
website="example.com"
- appDeveloperRegistryId="registry_id" />
+ registryId="registry_id" />
</transparency-info>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-developer-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-developer-info.xml
index 101c98b..d7a4e1a 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-developer-info.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-developer-info.xml
@@ -7,5 +7,6 @@
<string name="country_region" value="US"/>
<long name="relationship" value="5"/>
<string name="website" value="example.com"/>
+ <string name="app_developer_registry_id" value="registry_id"/>
</pbundle_as_map>
</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
index 36beb93..8f854ad1 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
@@ -1,6 +1,4 @@
<app-metadata-bundles version="123">
- <system-app-safety-label url="www.example.com">
- </system-app-safety-label>
<safety-labels version="12345">
<data-labels>
<data-shared dataCategory="location"
@@ -21,6 +19,8 @@
<third-party-verification url="www.example.com">
</third-party-verification>
</safety-labels>
+ <system-app-safety-label url="www.example.com">
+ </system-app-safety-label>
<transparency-info>
<developer-info
name="max"
@@ -29,7 +29,7 @@
countryRegion="US"
relationship="aosp"
website="example.com"
- appDeveloperRegistryId="registry_id" />
+ registryId="registry_id" />
<app-info title="beervision" description="a beer app" containsAds="true" obeyAps="false" adsFingerprinting="false" securityFingerprinting="false" privacyPolicy="www.example.com" securityEndpoints="url1|url2|url3" firstPartyEndpoints="url1" serviceProviderEndpoints="url55|url56" category="Food and drink" email="max@maxloh.com" />
</transparency-info>
</app-metadata-bundles>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
index db21280..8f1dc64 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
@@ -42,6 +42,7 @@
<string name="country_region" value="US"/>
<long name="relationship" value="5"/>
<string name="website" value="example.com"/>
+ <string name="app_developer_registry_id" value="registry_id"/>
</pbundle_as_map>
<pbundle_as_map name="app_info">
<string name="title" value="beervision"/>